home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / DCLAP 6d / dclap6d / network / nsclilib.old / ni_lib.c < prev    next >
Encoding:
Text File  |  1996-07-05  |  83.1 KB  |  2,676 lines  |  [TEXT/R*ch]

  1. /*
  2. * ===========================================================================
  3. *
  4. *                            PUBLIC DOMAIN NOTICE
  5. *               National Center for Biotechnology Information
  6. *
  7. *  This software/database is a "United States Government Work" under the
  8. *  terms of the United States Copyright Act.  It was written as part of
  9. *  the author's official duties as a United States Government employee and
  10. *  thus cannot be copyrighted.  This software/database is freely available
  11. *  to the public for use. The National Library of Medicine and the U.S.
  12. *  Government have not placed any restriction on its use or reproduction.
  13. *
  14. *  Although all reasonable efforts have been taken to ensure the accuracy
  15. *  and reliability of the software and data, the NLM and the U.S.
  16. *  Government do not and cannot warrant the performance or results that
  17. *  may be obtained by using this software or data. The NLM and the U.S.
  18. *  Government disclaim all warranties, express or implied, including
  19. *  warranties of performance, merchantability or fitness for any particular
  20. *  purpose.
  21. *
  22. *  Please cite the author in any work or product based on this material.
  23. *
  24. * ===========================================================================
  25. *
  26. * File Name:    ni_lib.c
  27. *
  28. * Author:       Beatty, Gish, Epstein
  29. *
  30. * Version Creation Date:        1/1/92
  31. *
  32. * $Revision: 1.36 $
  33. *
  34. * File Description:
  35. *   This file is a library of functions to be used by server application
  36. *   and client software, using the NCBI "network services" paradigm.
  37. *
  38. *
  39. * Modifications:
  40. * --------------------------------------------------------------------------
  41. * Date     Name        Description of modification
  42. * -------  ----------  -----------------------------------------------------
  43. * 4/27/92  Epstein     Added extensive in-line commentary, and removed all tabs.
  44. * 5/11/92  Epstein     Removed unused function NI_SVCRequestGet(); added support
  45. *                      for the connection ID to be written to a CONID file each
  46. *                      time that the value of conid is updated; in practice,
  47. *                      only dispatcher will update a CONID file.
  48. * 6/22/92  Epstein     For UNIX signals, catch the SIGPIPE error which can
  49. *                      occur when writing to a socket which is no longer
  50. *                      connected.
  51. * 7/06/92  Epstein     Changed sokselectw() to examine the SO_ERROR socket option
  52. *                      after select()-ing a socket to which we were attempting a
  53. *                      connection. This eliminates "false connects", i.e.,
  54. *                      unsuccessful connection attempts which look successful
  55. *                      because the select() call returns 1.
  56. * 7/14/92  Epstein     Changed NI_SetDispatcher() and NI_InitServices() to use
  57. *                      a configurable timeout parameter, and in the process
  58. *                      also changed sokselectw() to have a timeout parameter,
  59. * 1/21/93  Epstein     Add dispatcher-list support, and add dispatcher-list
  60. *                      parameter to NI_InitServices().
  61. * 2/12/93  Epstein     Use new boolean parameter to MsgMakeHandle(), indicating
  62. *                      whether or not it should create a socket.  This was
  63. *                      an attempted fix for a Mac problem ... it later
  64. *                      turned out to be an incorrect problem-fix, but also
  65. *                      does no harm.
  66. * 2/24/93  Epstein     Fix long-standing Mac bug, by correctly destroying
  67. *                      services handle and hence closing an open socket.
  68. * 3/02/93  Epstein     Add functions to write dispatcher-configuration info
  69. *                      to a config file.  This provides a standardized
  70. *                      mechanisms which applications may use for net services
  71. *                      configuration.  Also added platform functions, so
  72. *                      that dispatcher/server complex can know what type
  73. *                      of platform a client is running on, assuming that the
  74. *                      client is telling the truth.
  75. * 3/03/93  Epstein     Cleanup variable initialization.
  76. * 3/08/93  Epstein     Improve error messages & cleanup to NI_InitServices,
  77. *                      include reason in login failure message, and add
  78. *                      client platform to service request.
  79. * 3/09/93  Epstein     Add HaltServices() function to simplify cleanup.
  80. * 3/22/93  Epstein     Fix typecast for getsockopt(), and, more importantly,
  81. *                      remember to return the computed value in NI_GetPlatform.
  82. * 3/23/93  Epstein     Support VMS/TGV, and add NETP_INET_ prefixes to 
  83. *                      conditional-compilation symbols.
  84. * 3/24/93  Epstein     Clear the caller's pointer in NI_SetDispConfig().
  85. * 3/31/93  Epstein     Add dispatcher pointer as context for all network
  86. *                      services operations; this allows an application
  87. *                      to use more than one dispatcher at a time, at the
  88. *                      expense of slightly greater complexity.  Also add
  89. *                      a "Generic Init" function, which can be used by
  90. *                      an application to obtain network-services in a
  91. *                      simplified, standardized manner.
  92. * 3/31/93  Epstein     Move debug and module variables to their correct home.
  93. * 4/02/93  Epstein     Add WinSock support.
  94. * 4/12/93  Schuler     Add MAKEWORD macro.
  95. * 4/21/93  Schuler     Removed function prototypes for NI_AsnRead, NI_AsnWrite
  96. * 5/07/93  Epstein     Move WSAStartup() code to a better place, add workaround
  97. *                      for connection attempt on a non-blocking socket in PC-NFS
  98. *                      4.0, add more platform definitions.
  99. * 5/24/93  Epstein     Add separate error codes for TCP/IP initialization
  100. *                      failure and inability to resolve local host name.
  101. * 5/25/93  Epstein     Add configuration-file workaround for PC-NFS 5.0 bug,
  102. *                      where NIS sometimes fails on the PC's own host name.
  103. * 5/27/93  Epstein     Incorporate pragmatic "Gestalt" code for Vibrant
  104. *                      scrolling workaround for WinSock under Windows 3.1,
  105. *                      add add SOCK_INDEX_ERRNO macro to workaround another
  106. *                      WinSock pecularity.
  107. * 6/02/93  Schuler     Change "Handle" to "MonitorPtr" for Monitors.
  108. * 6/07/93  Epstein     Added generic timer functions.
  109. *                      Also add missing revision history, derived from
  110. *                      RCS file.
  111. * 6/09/93  Epstein     Added activity hook to report network activity back
  112. *                      to an application.
  113. * 6/14/93  Epstein     Changed "Generic" logic to cause UNIX/VMS loginname
  114. *                      to override loginname, rather than vice versa.  Also
  115. *                      setup DispatchConnect() logic to set client's declared
  116. *                      IP address to 0.0.0.0, rather than causing an error,
  117. *                      in the case where the client cannot resolve its own
  118. *                      host name.  In this case, the dispatcher will set its
  119. *                      own opinion of the client address based upon
  120. *                      getpeername().
  121. * 6/15/93  Epstein     Eliminate "Gestalt" code for Vibrant scrolling
  122. *                      workaround for WinSock under Windows 3.1, since the
  123. *                      solution for this problem does not require its use.
  124. * 6/25/93  Epstein     Fix activity-hook action for service disconnection (had
  125. *                      erroneously announced dispatcher-disconnection), and
  126. *                      add logic to try to avoid getservbyname() by looking
  127. *                      up dispatcher port # and (loport,hiport) in NCBI
  128. *                      configuration file instead of in NIS.  As a last resort,
  129. *                      look up the name in NIS if the entry in the NCBI
  130. *                      config. file is non-numeric.  Also, change the client
  131. *                      port lookup mechanism for Macintoshes to add a configured
  132. *                      "delta" value to the low port number.  This results in
  133. *                      allowing several Network Services applications to run
  134. *                      concurrently on a Mac without port conflicts.
  135. * 7/08/93  Epstein     Fix list traversal error in NI_ProcessTimers()
  136. * 7/08/93  Epstein     Added a counter as a failsafe mechanism in
  137. *                      NI_ProcessTimers(), since previous fix attempt failed.
  138. * 7/09/93  Epstein     Changed a few #define names to avoid Alpha compilation
  139. *                      warnings, and added reference count to dispatcher data
  140. *                      structure.
  141. * 8/09/93  Epstein     Improve diagnostics when a listen() call fails
  142. * 8/23/93  Epstein     Add currentDisp variable so that the currently-attached
  143. *                      dispatcher is used when the parameter to NI_SetDispatcher
  144. *                      is NULL.
  145. * 8/31/93  Epstein     Fix host vs. network order when comparing port numbers.
  146. * 9/08/93  Epstein     Added new stackDescription variable, to be able to
  147. *                      report to the dispatcher the identity of the vendor
  148. *                      of the WinSock stack
  149. * 9/09/93  Epstein     Fix use of currentDisp variable to correctly compare
  150. *                      new dispatcher request again current dispatcher.
  151. * ==========================================================================
  152. */
  153.  
  154. #define _NCBINET_LOCAL_VARS
  155. #define __NI_LIB__
  156. #include "ncbinet.h"
  157. #include "ni_lib.h"
  158. #include "ni_msg.h"
  159. #ifdef OS_UNIX
  160. #include <signal.h>
  161. #endif /* OS_UNIX */
  162. #ifdef OS_MAC
  163. #include <neterrno.h> /* include missing error numbers */
  164. #endif /* OS_MAC */
  165. #ifdef OS_VMS
  166. #include <perror.h>
  167. #endif /* OS_VMS */
  168.  
  169.  
  170. #ifdef NETP_INET_NEWT
  171.  
  172. #define SIN_ADDR        sin_addr.S_un.S_addr
  173. #define H_ADDR_TYPE     Uint4Ptr
  174. #else
  175. #define SIN_ADDR        sin_addr
  176. #define H_ADDR_TYPE     struct in_addr *
  177. #endif
  178.  
  179. #ifdef WIN16
  180. #ifndef MAKEWORD
  181. #define MAKEWORD(a,b)   ((WORD)(((BYTE)(a)) | ((WORD)((BYTE)(b))) <<8)) 
  182. #endif 
  183. #endif
  184.  
  185.  
  186. typedef struct NI_Timer {
  187.     time_t timeout;
  188.     NI_TimeoutHook hook;
  189.     Pointer hookParam;
  190. } NI_Timer, PNTR NI_TimerPtr;
  191.  
  192.  
  193. /* GLOBALS */
  194. static FILE             *conid_fp = NULL;           /* File pointer for CONID */
  195. static NodePtr timerHead = NULL;                    /* list of timers */
  196. static NI_NetServHook activityHook = NULL;
  197. static NI_DispatcherPtr currentDisp = NULL;
  198. static CharPtr stackDescription = NULL;
  199.  
  200.  
  201. #ifdef NETP_INET_WSOCK
  202. static Int4 wsaStartupCount = 0;
  203. #endif
  204.  
  205. NILoginPtr              NI_MakeMsgLogin PROTO((void));
  206. static Int2             SetIdentity PROTO((NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain));
  207. static void             HaltServices PROTO((NI_DispatcherPtr disp));
  208. static NI_HandPtr       DispatchConnect PROTO((NI_DispatcherPtr disp, CharPtr host, CharPtr name, int timeout));
  209. static Uint2            bindPort PROTO((int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport));
  210. static Int2             CopyIdentity PROTO((NI_DispatcherPtr disp, NI_UidPtr uid));
  211.  
  212. /** ( these are prototyped in ni_msg.h   [GDS] )
  213. Int2                    NI_AsnRead PROTO((Pointer fd, CharPtr buf, Uint2 len));
  214. Int2                    NI_AsnWrite PROTO((Pointer fd, CharPtr buf, Uint2 len));
  215. **/
  216. int                     sokselectr PROTO((int fd));
  217. int                     sokselectw PROTO((int fd, int timeout));
  218.  
  219. int                     getAsnError PROTO((char * str));
  220. void                    SetConFilePtr PROTO((FILE *fp));
  221. void                    CloseConFile PROTO((void));
  222.  
  223.  
  224.  
  225. /*
  226.  * Purpose:     Specify which dispatcher a client should try to connect to
  227.  *
  228.  * Parameters:
  229.  *   disp           Usually NULL, the pointer to a pre-existing Dispatcher
  230.  *                  structure
  231.  *   host           Name of the host (Fully Qualified Domain Name) to use
  232.  *   svc            Name of the "service" to try to use on that host
  233.  *   dispserialnum  Serial number of dispatcher-list. Use -1 if no response
  234.  *                  list is desired, or 0 if the serial number is not known.
  235.  *
  236.  *
  237.  * Description:
  238.  *              Set up the dispatcher name which should be used, and the
  239.  *              name of the service on that dispatcher. If other parameters
  240.  *              have been specified previously, free the memory associated
  241.  *              with those names.
  242.  *
  243.  * Note:
  244.  *              There are useful defaults for "svc". When in doubt, call
  245.  *              this function with a second arguement of NULL.
  246.  */
  247.  
  248. NI_DispatcherPtr
  249. NI_SetDispatcher(NI_DispatcherPtr disp, CharPtr host, CharPtr svc, int timeout,
  250.                  Int4 dispserialnum)
  251. {
  252.     if (disp == NULL) {
  253.         if (currentDisp != NULL)
  254.         { /* use current dispatcher if it matches what the caller wants */
  255.             if (StringCmp(host, currentDisp->dispServiceName) == 0 &&
  256.                 StringCmp(svc, currentDisp->dispHostName) == 0) {
  257.                 return currentDisp;
  258.             }
  259.         }
  260.  
  261.         disp = (NI_DispatcherPtr) MemNew(sizeof(NI_Dispatcher));
  262.         if (disp == NULL)
  263.             return NULL;
  264.         disp->reqResponse = NULL;
  265.         disp->dispHostName = NULL;
  266.         disp->dispServiceName = NULL;
  267.         disp->dispSerialNo = 0;
  268.         disp->localHostAddr[0] = '\0';
  269.         disp->dispHP = NULL;
  270.         disp->svcsHP = NULL;
  271.         disp->clientPort = 0;
  272.         disp->identity = NULL;
  273.         disp->dispTimeout = 0;
  274.         disp->referenceCount = 0;
  275.     }
  276.     if (disp->dispHostName != NULL) {
  277.         MemFree(disp->dispHostName);
  278.         disp->dispHostName = NULL;
  279.     }
  280.     if (disp->dispServiceName != NULL) {
  281.         MemFree(disp->dispServiceName);
  282.         disp->dispServiceName = NULL;
  283.     }
  284.     if (host != NULL)
  285.         disp->dispHostName = StringSave(host);
  286.     if (svc != NULL)
  287.         disp->dispServiceName = StringSave(svc);
  288.  
  289.     disp->dispSerialNo = dispserialnum;
  290.     disp->dispTimeout = timeout;
  291.  
  292.     return disp;
  293. } /* NI_SetDispatcher */
  294.  
  295.  
  296.  
  297. /*
  298.  * Purpose:     Try to establish a connection to the dispatcher
  299.  *
  300.  * Parameters:
  301.  *   disp           A pointer to the dispatcher structure
  302.  *   user           User name to try on the dispatcher
  303.  *   group          Group name to try on the dispatcher
  304.  *   password       Password for this user name
  305.  *   dip            A pointer to the caller's list of dispatchers; this should
  306.  *                     be used by the caller to update its information
  307.  *                     regarding which dispatchers to try in the future
  308.  *                     (if dip == NULL, then no retries will be made to get
  309.  *                      alternate dispatchers)
  310.  *
  311.  * Returns:
  312.  *                 -1, if something failed (ni_errno indicates the nature of
  313.  *                     the problem)
  314.  *                 0, if everything was successful
  315.  *                 1, if we are connected to the dispatcher which we requested,
  316.  *                     but the list of current dispatchers has changed
  317.  *                 2, if we are connected to a dispatcher, but not the one
  318.  *                     which we requested
  319.  *
  320.  *
  321.  * Description:
  322.  *              Connect to the dispatcher
  323.  *              Set-up a socket for an incoming connection from a server
  324.  *                  application process
  325.  *              Send a LOGIN message to the dispatcher
  326.  *              Wait for an ACK or NACK response from the dispatcher (or for
  327.  *                  a timeout to occur)
  328.  *              If the response was a NACK due to the dispatcher being a
  329.  *                  backup dispatcher, then try the dispatcher which it
  330.  *                  directs us to
  331.  */
  332.  
  333. Int2
  334. NI_InitServices(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr password, NI_DispInfoPtr PNTR dip)
  335. {
  336.     NIMsgPtr            mp, imp;
  337.     NILoginPtr          loginp;
  338.     struct sockaddr_in  svcsAddr;
  339.     struct timeval      timeout;
  340.     int                 ready;
  341.     NIDispInfoPtr       dispinfo = NULL;
  342.     Boolean             newDispToTry;
  343.     Int2                altDispTries = 0;
  344.     Int2                retval = 0;
  345.     int                 status;
  346.     fd_set              readfds;
  347.  
  348. #ifdef NETP_INET_WSOCK
  349.     WSADATA wsaData;
  350.  
  351.     status = WSAStartup(MAKEWORD(1,1),&wsaData);
  352.     /* Try WinSock 1.1 and 1.0 in that order of preference */
  353.     if (status != 0 && (status = WSAStartup(MAKEWORD(1,0),&wsaData)) != 0)
  354.     {
  355.         TRACE("WSAStartup failed (code %d)\n", status);
  356.         ni_errno = NIE_TCPINITFAIL;
  357.         return -1;
  358.     }
  359.     TRACE("%s\n", wsaData.szDescription);
  360.     if (stackDescription != NULL)
  361.     {
  362.         MemFree(stackDescription);
  363.     }
  364.     stackDescription = StringSave(wsaData.szDescription);
  365.     wsaStartupCount++;
  366.  
  367. #endif
  368.  
  369.     if (disp == NULL)
  370.     {
  371.         ni_errno = NIE_MISC;
  372.         return -1;
  373.     }
  374.  
  375.     if (disp->referenceCount > 0 && disp->dispHP != NULL)
  376.     { /* already connected */
  377.         disp->referenceCount++;
  378.         return 0;
  379.     }
  380.  
  381.     if (disp->dispHostName == NULL)
  382.         disp->dispHostName = StringSave(NI_DEFAULT_HOST);
  383.     if (disp->dispServiceName == NULL)
  384.         disp->dispServiceName = StringSave(NI_DEFAULT_SERVICE);
  385.  
  386.     do {
  387.         newDispToTry = FALSE;
  388.         disp->svcsHP = NULL;
  389.         if ((disp->dispHP = DispatchConnect(disp, disp->dispHostName, disp->dispServiceName, disp->dispTimeout))
  390.             == NULL) {
  391.             NI_DestroyDispInfo(dispinfo);
  392.             HaltServices (disp);
  393.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: Unable to connect to host <%s>, error <%s>", disp->dispHostName, ni_errlist[ni_errno]);
  394.             return -1;      /* ni_errno remains set */
  395.         }
  396.  
  397.         if ((disp->svcsHP = MsgMakeHandle(TRUE)) == NULL) {
  398.             NI_DestroyDispInfo(dispinfo);
  399.             HaltServices (disp);
  400.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: Unable to allocate resources to communicate with %s", disp->dispHostName);
  401.             return -1;
  402.         }
  403.  
  404.         if (disp->dispTimeout > 0)
  405.         {
  406.             MsgSetReadTimeout(disp->svcsHP, disp->dispTimeout);
  407.         }
  408.         if ((disp->clientPort = bindPort(disp->svcsHP->sok, &svcsAddr, disp->loport, disp->hiport)) == 0) {
  409.             MsgDestroyHandle(disp->svcsHP);
  410.             disp->svcsHP = NULL;
  411.             ni_errno = NIE_NOBIND;                  /* can't bind a free application socket */
  412.             NI_DestroyDispInfo(dispinfo);
  413.             HaltServices (disp);
  414.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  415.             return -1;
  416.         }
  417.         if ((status = listen(disp->svcsHP->sok, 5)) < 0) {
  418. #ifdef NETP_INET_NEWT
  419.             SOCK_ERRNO = ABS(status);
  420. #endif
  421.             StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  422.             ni_errno = NIE_NOLISTEN;
  423.             NI_DestroyDispInfo(dispinfo);
  424.             HaltServices (disp);
  425.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s> <port %d, errno %d>", ni_errlist[ni_errno], (int) disp->clientPort, (int) SOCK_ERRNO);
  426.             return -1;
  427.         }
  428.  
  429.         SetIdentity(disp, user, group, NI_DEFAULT_DOMAIN);
  430.  
  431.         loginp = NI_MakeMsgLogin();
  432.         NI_DestroyUid(loginp->uid);
  433.         loginp->uid = NI_MakeUid();
  434.         loginp->seqno = disp->dispHP->seqno++;
  435.         loginp->dispserialno = disp->dispSerialNo;
  436.         CopyIdentity(disp, loginp->uid);
  437.         if (password != NULL)
  438.             loginp->password = StringSave(password);
  439.         mp = MsgBuild(NI_LOGIN, disp->dispHP->conid, (VoidPtr) loginp);
  440.  
  441.         if (MsgWrite(disp->dispHP, mp) < 0) {
  442.             if (getAsnError(ni_errtext) == ECONNRESET)
  443.                 ni_errno = NIE_MAXCONNS;
  444.             else
  445.                 ni_errno = NIE_MSGWRITE;
  446.             MsgDestroyHandle(disp->svcsHP);
  447.             disp->svcsHP = NULL;
  448.             NI_DestroyDispInfo(dispinfo);
  449.             HaltServices (disp);
  450.             ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  451.             return -1;
  452.         }
  453.  
  454.         /* blocks until ACK or ERROR from dispatcher or TIMEOUT */
  455.  
  456.         timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  457.         timeout.tv_usec = 0;
  458.         FD_ZERO(&readfds);
  459.         FD_SET(disp->dispHP->sok, &readfds);
  460.         while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  461.             if (SOCK_ERRNO == EINTR)
  462.                 ;                   /* repeat while interrupted */
  463.             else {
  464.                 MsgDestroyHandle(disp->svcsHP);
  465.                 disp->svcsHP = NULL;
  466.                 ni_errno = NIE_SELECT;              /* select error */
  467.                 NI_DestroyDispInfo(dispinfo);
  468.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  469.                 return -1;
  470.             }
  471.         }
  472.  
  473.         if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  474.             if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  475.                 if (getAsnError(ni_errtext) == ECONNRESET)
  476.                     ni_errno = NIE_MAXCONNS;
  477.                 else
  478.                     ni_errno = NIE_MSGREAD;
  479.  
  480.                 MsgDestroyHandle(disp->svcsHP);
  481.                 disp->svcsHP = NULL;
  482.                 NI_DestroyDispInfo(dispinfo);
  483.                 HaltServices (disp);
  484.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  485.                 return -1;
  486.             }
  487.             switch (imp->type) {
  488.               case NI_ACK:
  489.                 /************************************************************/
  490.                 /* even though we connected successfully to the dispatcher, */
  491.                 /* it may have given us more up-to-date information on the  */
  492.                 /* latest list of dispatchers which should be tried; if so, */
  493.                 /* pass the updated list back to the caller                 */
  494.                 /************************************************************/
  495.                 if (imp->msun.ack->dispinfo != NULL) {
  496.                     if (dispinfo != NULL)
  497.                     {
  498.                         NI_DestroyDispInfo(dispinfo);
  499.                         dispinfo = NULL;
  500.                     }
  501.                     dispinfo = imp->msun.ack->dispinfo;
  502.                     imp->msun.ack->dispinfo = NULL; /* for clean free */
  503.                 }
  504.                 if (dispinfo != NULL && dip != NULL) {
  505.                     if (*dip != NULL)
  506.                         NI_DestroyDispInfo((NIDispInfoPtr) *dip);
  507.                     *dip = (NI_DispInfoPtr) dispinfo;
  508.                     dispinfo = NULL;
  509.                     retval = 1;
  510.                 }
  511.                 else {
  512.                     NI_DestroyDispInfo(dispinfo);
  513.                 }
  514.                 MsgDestroy(imp);
  515. #ifdef OS_UNIX
  516.                 signal(SIGPIPE, SIG_IGN); /* catch socket errors */
  517. #endif /* OS_UNIX */
  518.                 disp->referenceCount++;
  519.                 if (currentDisp == NULL)
  520.                 {
  521.                     currentDisp = disp;
  522.                 }
  523.                 return retval;   /* only good return */
  524.  
  525.               case NI_NACK:
  526.                 ni_errno = (enum ni_error) imp->msun.nack->code;
  527.                 if (imp->msun.nack->reason != NULL)
  528.                 {
  529.                     StringCpy(ni_errtext, imp->msun.nack->reason);
  530.                 } else {
  531.                     ni_errtext[0] = '\0';
  532.                 }
  533.                 if (dispinfo != NULL)
  534.                 {
  535.                     NI_DestroyDispInfo(dispinfo);
  536.                     dispinfo = NULL;
  537.                 }
  538.                 dispinfo = imp->msun.nack->dispinfo;
  539.                 imp->msun.nack->dispinfo = NULL; /* for clean free */
  540.                 if (ni_errno == NIE_BACKUPDISP && dispinfo != NULL &&
  541.                     dispinfo->numdispatchers > 0 && dip != NULL &&
  542.                     ++altDispTries < MAX_ALT_DISP_TRIES)
  543.                 {
  544.                     MsgDestroy(imp);
  545.                     HaltServices (disp);
  546.                     NI_SetDispatcher(disp, dispinfo->displist[0], disp->dispServiceName,
  547.                                      disp->dispTimeout, dispinfo->serialno);
  548.                     newDispToTry = TRUE;
  549.                     retval = 2;
  550.                     break;
  551.                 }
  552.                 MsgDestroy(imp);
  553.                 MsgDestroyHandle(disp->svcsHP);
  554.                 disp->svcsHP = NULL;
  555.                 NI_DestroyDispInfo(dispinfo);
  556.                 HaltServices (disp);
  557.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s> %s", ni_errlist[ni_errno], ni_errtext);
  558.                 return -1;
  559.  
  560.               default:
  561.                 MsgDestroy(imp);
  562.                 ni_errno = NIE_MSGUNK;
  563.                 MsgDestroyHandle(disp->svcsHP);
  564.                 disp->svcsHP = NULL;
  565.                 NI_DestroyDispInfo(dispinfo);
  566.                 HaltServices (disp);
  567.                 ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  568.                 return -1;
  569.             }
  570.         }
  571.     } while (newDispToTry);
  572.  
  573.     MsgDestroyHandle(disp->svcsHP);
  574.     disp->svcsHP = NULL;
  575.     ni_errno = NIE_LOGTIMEOUT;          /* TIMEOUT */
  576.     NI_DestroyDispInfo(dispinfo);
  577.     HaltServices (disp);
  578.     ErrPost (CTX_NCBICORE, CORE_UNKNOWN, "NI_InitServices: <%s>", ni_errlist[ni_errno]);
  579.     return -1;
  580. } /* NI_InitServices */
  581.  
  582.  
  583. /*
  584.  * Purpose:     Init network services based on information in config file
  585.  *
  586.  * Parameters:
  587.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  588.  *                     to "NCBI"
  589.  *   configSection  Section with NCBI-style configuration file.  If NULL,
  590.  *                     defaults to "NET_SERV"
  591.  *   showMonitor    Boolean; if TRUE, display a monitor while re-trying
  592.  *                     for an alternate dispatcher
  593.  *   lastDispatcher Pointer to where this function should store the name
  594.  *                  of the dispatcher which was actually used (may be NULL
  595.  *                  if the caller does not care about this value)
  596.  *   lastDispLen    Maximum length of lastDispatcher
  597.  *
  598.  * Returns:
  599.  *                 NULL, if unable to contact dispatcher
  600.  *                 a pointer to the Dispatcher structure, otherwise
  601.  *
  602.  *
  603.  * Description:
  604.  *              Extracts a dispatcher name and a user name from a configuration
  605.  *              file.  If necessary, tries other dispatchers, in order, as
  606.  *              listed in configuration file.
  607.  *
  608.  *
  609.  * Note:
  610.  *              This function is provided as a convenience to developers who
  611.  *              wish to use Network Services.  Use of this function is not
  612.  *              integral to the use of Network Services ... it is merely a
  613.  *              convenience.
  614.  */
  615.  
  616. NI_DispatcherPtr
  617. NI_GenericInit (CharPtr configFile, CharPtr configSection, Boolean showMonitor, CharPtr lastDispatcher, Int2 lastDispLen)
  618. {
  619.     char *def_user;
  620.     char username[64];
  621.     char groupname[20];
  622.     char password[20];
  623.     char dispname[60];
  624.     char disp_config[10];
  625.     char disp_msg[50];
  626.     char buf[60];
  627.     Boolean more_disps;
  628.     int alternate = 1;
  629.     int disp_timeout;
  630.     Int4 disp_serialno;
  631.     Monitor *mon = NULL;
  632.     NI_DispInfoPtr dip = NULL;
  633.     NI_DispatcherPtr disp = NULL;
  634. #ifdef OS_UNIX
  635.     char *getlogin PROTO((void));
  636. #endif
  637.  
  638.     /******************* open the network connnection *********/
  639. #define NI_DISP_NAME "dispatch1.nlm.nih.gov"
  640. #define NI_USER_NAME "anonymous"
  641. #define NI_GROUP_NAME "GUEST"
  642.  
  643.     def_user = NI_USER_NAME;
  644.  
  645.     if (configFile == NULL)
  646.         configFile = "NCBI";
  647.     if (configSection == NULL)
  648.         configSection = "NET_SERV";
  649.  
  650.     GetAppParam(configFile, configSection, "DISP_USERNAME", NI_USER_NAME, username,
  651.                 sizeof username);
  652.     /* the user's login name overrides the config file */
  653.     /* for UNIX or VMS systems (or, for the future, any system where the      */
  654.     /* user name can be determined), use the user's login name as the default */
  655.     def_user = NULL;
  656. #ifdef OS_UNIX
  657.     def_user = getlogin();
  658. #endif
  659. #ifdef OS_VMS
  660.     def_user = getenv("USER");
  661. #endif
  662.     if (def_user != NULL)
  663.     {
  664.         StrNCpy(username, def_user, sizeof username);
  665.     }
  666.  
  667.     GetAppParam(configFile, configSection, "DISP_GROUPNAME", NI_GROUP_NAME, groupname,
  668.                 sizeof groupname);
  669.     GetAppParam(configFile, configSection, "DISP_PASSWORD", "", password,
  670.                 sizeof password); /* default = NONE */
  671.  
  672.     GetAppParam(configFile, configSection, "DISP_TIMEOUT", "0", buf, sizeof buf);
  673.     disp_timeout = atoi(buf);
  674.  
  675.     GetAppParam(configFile, configSection, "DISPSERIALNO", "0", buf, sizeof buf);
  676.     disp_serialno = atoi(buf);
  677.  
  678.     GetAppParam(configFile, configSection, "DISPATCHER", NI_DISP_NAME, dispname,
  679.                 sizeof dispname);
  680.  
  681.     do {
  682.         if (alternate == 2 && showMonitor)
  683.         {
  684.             mon = MonitorStrNew("Unable to contact primary dispatcher", 35);
  685.         }
  686.         if (alternate >= 2)
  687.         {
  688.             sprintf(disp_msg, "Trying dispatcher #%d <", alternate);
  689.             StrCat(disp_msg, dispname);
  690.             StrCat(disp_msg, ">");
  691.             if (showMonitor) {
  692.                 MonitorStrValue(mon, disp_msg);
  693.             }
  694.         }
  695.  
  696.         if (lastDispatcher != NULL) {
  697.                 StrNCpy(lastDispatcher, dispname, lastDispLen);
  698.         }
  699.  
  700.         disp = NI_SetDispatcher (NULL, dispname, NULL, disp_timeout, disp_serialno);
  701.  
  702.         if (NI_InitServices(disp, username, groupname[0] == '\0' ? NULL : groupname,
  703.                             password[0] == '\0' ? NULL : password, &dip) >= 0)
  704.         {
  705.             if (dip != NULL && dip->serialno != disp_serialno) {
  706.                 NI_SetDispConfig (&dip, dispname, sizeof dispname);
  707.             }
  708.             if (mon != NULL)
  709.                 MonitorFree(mon);
  710.             return disp;
  711.         }
  712.         ErrShow ();
  713.         NI_EndServices (disp);
  714.         sprintf(disp_config, "DISP_ALT_%d", alternate++);
  715.         more_disps = GetAppParam(configFile, configSection, disp_config, "", dispname,
  716.                                  sizeof dispname);
  717.     } while (more_disps);
  718.  
  719.     if (mon != NULL)
  720.         MonitorFree(mon);
  721.  
  722.     return NULL;
  723. }
  724.  
  725.  
  726. /*
  727.  * Purpose:     Get a network service based on information in config file
  728.  *
  729.  * Parameters:
  730.  *   disp           Pointer to the dispatcher structure obtained from a
  731.  *                  previous call to NI_SetDispatcher or NI_GenericInit
  732.  *   configFile     Name of NCBI-style configuration file.  If NULL, defaults
  733.  *                     to "NCBI"
  734.  *   defService     The default service/resource/resource-type name, if
  735.  *                  not specified otherwise in configuration file.
  736.  *   hasResource    Boolean; if TRUE, ask for a resource when requesting
  737.  *                     service
  738.  *
  739.  * Returns:
  740.  *                 NULL, if unable to obtain service
  741.  *                 a pointer to the service-structure, otherwise
  742.  *
  743.  *
  744.  * Description:
  745.  *              Extracts a service name and other service data from a
  746.  *              configuration file, and attempts to obtain that service.
  747.  *
  748.  *
  749.  * Note:
  750.  *              This function is provided as a convenience to developers who
  751.  *              wish to use Network Services.  Use of this function is not
  752.  *              integral to the use of Network Services ... it is merely a
  753.  *              convenience.
  754.  */
  755.  
  756. NI_HandPtr
  757. NI_GenericGetService (NI_DispatcherPtr disp, CharPtr configFile, CharPtr configSection, CharPtr defService, Boolean hasResource)
  758. {
  759.     char buf[40];
  760.     char service[40];
  761.     char resource[40];
  762.     char res_type[40];
  763.     int serv_min;
  764.     int serv_max;
  765.     int res_min;
  766.     int res_max;
  767.  
  768.     if (configFile == NULL)
  769.         configFile = "NCBI";
  770.  
  771.     GetAppParam(configFile, configSection, "SERVICE_NAME", defService,
  772.             service, sizeof service);
  773.     GetAppParam(configFile, configSection, "SERV_VERS_MIN", "1",
  774.             buf, sizeof buf);
  775.     serv_min = atoi(buf);
  776.     GetAppParam(configFile, configSection, "SERV_VERS_MAX", "0",
  777.             buf, sizeof buf);
  778.     serv_max = atoi(buf);
  779.  
  780.     res_min = 1;
  781.     res_max = 0;
  782.  
  783.     if (hasResource) {
  784.         GetAppParam(configFile, configSection, "RESOURCE_NAME", defService,
  785.                 resource, sizeof resource);
  786.         GetAppParam(configFile, configSection, "RESOURCE_TYPE", defService,
  787.                 res_type, sizeof res_type);
  788.         GetAppParam(configFile, configSection, "RES_VERS_MIN", "1",
  789.                 buf, sizeof buf);
  790.         res_min = atoi(buf);
  791.         GetAppParam(configFile, configSection, "RES_VERS_MAX", "0",
  792.                 buf, sizeof buf);
  793.         res_max = atoi(buf);
  794.     }
  795.  
  796.     return NI_ServiceGet(disp, service, serv_min, serv_max,
  797.                          hasResource ? resource : NULL, res_type, res_min,
  798.                          res_max);
  799. }
  800.  
  801.  
  802. /*
  803.  * Purpose:     Write dispatcher-configuration information to a config file
  804.  *
  805.  * Parameters:
  806.  *   dipp           A pointer to the caller's list of dispatchers, obtained
  807.  *                     from NI_InitServices()
  808.  *   dispatcher     The caller's dispatcher string
  809.  *   dispLen        Length of the caller's dispatcher string
  810.  *
  811.  * Returns:
  812.  *                 0, if bad parameters were provided
  813.  *                 the dispatcher-list serial number, otherwise
  814.  *
  815.  *
  816.  * Description:
  817.  *              Sets up the "NCBI" configuration file with the following
  818.  *              entries in the "NET_SERV" section:
  819.  *              * DISPATCHER is the primary dispatcher name
  820.  *              * DISP_ALT_n for every alternate dispatcher, 1 <= n, a smaller
  821.  *                n indicates a higher priority alternate dispatcher
  822.  *              * DISPSERIALNO is the serial number of the dispatcher list
  823.  *                obtained from a remote dispatcher. This serial number should
  824.  *                be unique for all time ... the dispatcher's serial number
  825.  *                must be changed whenever the master list is modified.
  826.  *
  827.  * Note:
  828.  *              This configuration mechanism is only _one_ recommended
  829.  *              mechanism for network services dispatcher configuration. The
  830.  *              application may perform this configuration in any manner
  831.  *              deemed appropriate by the application programmer.
  832.  *
  833.  *              The value returned by this function is the recommended value
  834.  *              for the dispserialno parameter in a subsequent call to
  835.  *              NI_InitDispatcher().
  836.  */
  837.  
  838. Int4
  839. NI_SetDispConfig(NI_DispInfoPtr PNTR dipp, CharPtr dispatcher, Int2 dispLen)
  840. {
  841.   Int2 num;
  842.   Char dispConfig[20];
  843.   char buf[10];
  844.   Int4 retval;
  845.   NI_DispInfoPtr dip;
  846.  
  847.   if (dipp == NULL || (dip = *dipp) == NULL)
  848.   {
  849.     if (dispatcher != NULL)
  850.     {
  851.       dispatcher[0] = '\0';
  852.     }
  853.     return 0;
  854.   }
  855.  
  856.   if (dip->numdispatchers > 0 && dip->displist != NULL)
  857.   {
  858.     StringNCpy (dispatcher, dip->displist[0], dispLen);
  859.     SetAppParam ("NCBI", "NET_SERV", "DISPATCHER", dip->displist[0]);
  860.   }
  861.  
  862.   for (num = 1; num < dip->numdispatchers; num++)
  863.   {
  864.     sprintf (dispConfig, "DISP_ALT_%d", num);
  865.     SetAppParam ("NCBI", "NET_SERV", dispConfig, dip->displist[num]);
  866.   }
  867.  
  868.   /* wipe out any extraneous old configuration */
  869.   for (num = dip->numdispatchers; num < 100; num++)
  870.   {
  871.     sprintf (dispConfig, "DISP_ALT_%d", num);
  872.     if (GetAppParam("NCBI", "NET_SERV", dispConfig, "", buf, sizeof buf) <= 0)
  873.     {
  874.       break;
  875.     }
  876.     SetAppParam ("NCBI", "NET_SERV", dispConfig, NULL);
  877.   }
  878.  
  879.   retval = dip->serialno;
  880.   sprintf (buf, "%ld", (long) dip->serialno);
  881.   SetAppParam ("NCBI", "NET_SERV", "DISPSERIALNO", buf);
  882.  
  883.   NI_DestroyDispInfo ((NIDispInfoPtr) dip);
  884.   *dipp = NULL;
  885.  
  886.   return retval;
  887. }
  888.  
  889.  
  890. /*
  891.  * Purpose:     End use of network services
  892.  *
  893.  * Parameters:
  894.  *   disp           A pointer to the dispatcher structure
  895.  *
  896.  * Returns:
  897.  *                 0 (always)
  898.  *
  899.  *
  900.  * Description:
  901.  *              Tear down the sockets and data structures associated with
  902.  *              the dispatcher and a server, and free all memory associated
  903.  *              with data structures.
  904.  */
  905.  
  906. Int2
  907. NI_EndServices(NI_DispatcherPtr disp)
  908. {
  909.     if (disp == NULL)
  910.         return 0;
  911.  
  912.     if (disp->referenceCount > 0)
  913.         disp->referenceCount--;
  914.  
  915.     if (disp == currentDisp)
  916.     {
  917.         currentDisp = NULL;
  918.     }
  919.  
  920.     HaltServices (disp);
  921.     NI_SetDispatcher(disp, NULL, NULL, 0, 0);               /* free mem */
  922.  
  923.     if (stackDescription != NULL)
  924.     {
  925.         MemFree(stackDescription);
  926.         stackDescription = NULL;
  927.     }
  928.  
  929.     MemFree(disp);
  930.  
  931.     return 0;
  932. } /* NI_EndServices */
  933.  
  934.  
  935.  
  936. /*
  937.  * Purpose:     Request a catalog from the dispatcher
  938.  *
  939.  * Parameters:
  940.  *   disp           A pointer to the dispatcher structure
  941.  *
  942.  * Returns:
  943.  *                 NULL, if unable to obtain the catalog
  944.  *                 a pointer to the received catalog data structure, otherwise
  945.  *
  946.  *
  947.  * Description:
  948.  *              Send a request to the dispatcher, requesting a catalog, and
  949.  *              wait (up to some timeout) for a response. The dispatcher's
  950.  *              response should either be that catalog, or a NACK.
  951.  */
  952.  
  953. NICatalogPtr
  954. NI_GetCatalog(NI_DispatcherPtr disp)
  955. {
  956.     NICatalogPtr        catp;
  957.     NIMsgPtr            mp, imp;
  958.     NICmdPtr            cmdp;
  959.     struct timeval      timeout;
  960.     int                 ready;
  961.     fd_set              readfds;
  962.  
  963.     if (disp == NULL)
  964.         return NULL;
  965.  
  966.     cmdp = (NICmdPtr) NI_MakeMsgCmd();
  967.     cmdp->seqno = disp->dispHP->seqno++;
  968.     cmdp->code = NI_SEND_CATALOG;
  969.     if ((mp = MsgBuild(NI_COMMAND, disp->dispHP->conid, (VoidPtr) cmdp)) == NULL) {
  970.         ni_errno = NIE_MISC;    /* unable to alloc mem for Msg */
  971.         return NULL;
  972.     }
  973.     if (MsgWrite(disp->dispHP, mp) < 0) {
  974.         ni_errno = NIE_MSGWRITE;
  975.         return NULL;
  976.     }
  977.  
  978.     /* blocks until response from dispatcher or TIMEOUT */
  979.  
  980.     timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  981.     timeout.tv_usec = 0;
  982.     FD_ZERO(&readfds);
  983.     FD_SET(disp->dispHP->sok, &readfds);
  984.     while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  985.         if (SOCK_ERRNO == EINTR)
  986.             ;                   /* repeat while interrupted */
  987.         else {
  988.             ni_errno = NIE_SELECT;              /* select error */
  989.             return NULL;
  990.         }
  991.     }
  992.  
  993.     if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  994.         if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  995.             NI_CLOSESOCKET(disp->dispHP->sok);
  996.             ni_errno = NIE_MSGREAD;
  997.             return NULL;
  998.         }
  999.         switch (imp->type) {
  1000.           case NI_CATALOG:
  1001.             catp = imp->msun.catalog;
  1002.             imp->msun.catalog = NULL;
  1003.             ni_errno = NIE_NO_ERROR;
  1004.             MsgDestroy(imp);
  1005.             return catp;
  1006.             break;
  1007.  
  1008.           case NI_NACK:
  1009.             ni_errno = (enum ni_error) imp->msun.nack->code;
  1010.             if (imp->msun.nack->reason != NULL)
  1011.                 StringCpy(ni_errtext, imp->msun.nack->reason);
  1012.             else
  1013.                 ni_errtext[0] = '\0';
  1014.             MsgDestroy(imp);
  1015.             return NULL;
  1016.  
  1017.           default:
  1018.             MsgDestroy(imp);
  1019.             ni_errno = NIE_MSGUNK;      /* Unknown MSG type */
  1020.             return NULL;
  1021.         }
  1022.     }
  1023.     ni_errno = NIE_CMDTIMEOUT;          /* TIMEOUT */
  1024.     return NULL;
  1025. } /* NI_GetCatalog */
  1026.  
  1027.  
  1028.  
  1029. /*
  1030.  * Purpose:     Create the data structure for a service request
  1031.  *
  1032.  * Parameters:
  1033.  *   disp           A pointer to the dispatcher structure
  1034.  *
  1035.  * Returns:
  1036.  *                 a pointer to the newly created data structure
  1037.  *
  1038.  *
  1039.  * Description:
  1040.  *              Allocate the memory for a service request data structure,
  1041.  *              and fill in some of the fields.
  1042.  * Note:
  1043.  *              There are two ways for a program to issue a service request:
  1044.  *              (1) Multi-step, general method (like IRS form 1040)
  1045.  *                * Build a request with NI_SVCRequestBuild()
  1046.  *                * Populate the request with a specific service request using
  1047.  *                  NI_RequestSetService()
  1048.  *                * Populate the request with zero or more resource requests
  1049.  *                  calling NI_RequestAddResource() once for every resource
  1050.  *                * Send the request with NI_ServiceRequest(), and (hopefully)
  1051.  *                  obtain a connection to a service provider
  1052.  *                * At some later time, delete the request (to save memory)
  1053.  *              (2) One-stop shopping, for simple requirement (like form 1040EZ)
  1054.  *                * Do everything for a service and up to one resource using
  1055.  *                  NI_ServiceGet()
  1056.  */
  1057.  
  1058. NI_ReqPtr
  1059. NI_SVCRequestBuild(NI_DispatcherPtr disp)
  1060. {
  1061.     NI_ReqPtr   reqp;
  1062.  
  1063.     if (disp == NULL)
  1064.         return NULL;
  1065.  
  1066.     reqp = (NI_ReqPtr) NI_MakeRequest();
  1067.     reqp->clientPort = (Uint2) disp->clientPort;
  1068.     reqp->clientAddr = StringSave(disp->localHostAddr);
  1069.     reqp->dispatcher = disp; /* should not be freed when destroying Req */
  1070.  
  1071.     return  reqp;
  1072. } /* NI_SVCRequestBuild */
  1073.  
  1074.  
  1075.  
  1076. /*
  1077.  * Purpose:     Destroy a service request data structure
  1078.  *
  1079.  * Parameters:
  1080.  *   reqp         A pointer to the data structure to be destroyed
  1081.  *
  1082.  *
  1083.  * Description:
  1084.  *              Free all the resources associated with a service request
  1085.  */
  1086.  
  1087. void
  1088. NI_SVCRequestDestroy(NI_ReqPtr reqp)
  1089. {
  1090.     NI_DestroyRequest(reqp);
  1091. } /* NI_SVCRequestDestroy */
  1092.  
  1093.  
  1094.  
  1095. /*
  1096.  * Purpose:     Make a service request for a service and up to one resource
  1097.  *
  1098.  * Parameters:
  1099.  *   disp         A pointer to the dispatcher structure
  1100.  *   svc          Name of requested service
  1101.  *   svcvermin    Minimum version number requested for this service
  1102.  *   svcvermax    Maximum version number requested for this service
  1103.  *   res          Name of requested resource (possibly NULL)
  1104.  *   resvermin    Minimum version number requested for this resource
  1105.  *   resvermax    Maximum version number requested for this resource
  1106.  *
  1107.  * Returns:
  1108.  *                The result of the service request
  1109.  *
  1110.  *
  1111.  * Description:
  1112.  *              Create and issue a service request for the specified
  1113.  *              parameters.
  1114.  */
  1115.  
  1116. NI_HandPtr
  1117. NI_ServiceGet(NI_DispatcherPtr disp, CharPtr svc, Uint2 svcvermin, Uint2 svcvermax, CharPtr res, CharPtr restype, Uint2 resvermin, Uint2 resvermax)
  1118. {
  1119.     NI_ReqPtr   reqp;
  1120.  
  1121.     if (disp == NULL)
  1122.         return NULL;
  1123.  
  1124.     reqp = NI_SVCRequestBuild(disp);
  1125.     NI_RequestSetService(reqp, svc, svcvermin, svcvermax);
  1126.     if (res != NULL)
  1127.         NI_RequestAddResource(reqp, res, restype, resvermin, resvermax);
  1128.  
  1129.     return NI_ServiceRequest(reqp);
  1130. } /* NI_ServiceGet */
  1131.  
  1132.  
  1133.  
  1134. /*
  1135.  * Purpose:     Issue the specified service request
  1136.  *
  1137.  * Parameters:
  1138.  *   req          The pre-formatted service request
  1139.  *
  1140.  * Returns:
  1141.  *               A message handle to the server which is servicing our request,
  1142.  *                   if successful
  1143.  *               NULL, otherwise (ni_errno will indicate a more precise cause)
  1144.  *
  1145.  *
  1146.  * Description:
  1147.  *              Create and issue a service request for the specified
  1148.  *              service request, as follows:
  1149.  *              * Create and listen on a socket on which the server should
  1150.  *                respond
  1151.  *              * Send the service request to the dispatcher
  1152.  *              * Wait for the following two events, in either order:
  1153.  *                (1) A response from the dispatcher, which is either a
  1154.  *                    SVC_RESPONSE (good), or a NACK (bad) {or a timeout}
  1155.  *                (2) A connection request from the server, which we then
  1156.  *                    accept()
  1157.  *              * If both of the two events occur successfully, return with
  1158.  *                success, else, return with failure.
  1159.  */
  1160.  
  1161. NI_HandPtr
  1162. NI_ServiceRequest(NI_ReqPtr req)
  1163. {
  1164.     NI_HandPtr          sconnhp;
  1165. #ifdef OS_MAC
  1166.     Int4                sconnlen;
  1167. #else
  1168.     int                 sconnlen;
  1169. #endif
  1170.     struct sockaddr_in  sconnaddr;
  1171.     NIMsgPtr            mp, imp;
  1172.     NISvcReqPtr         svcreqp;
  1173.     struct timeval      timeout;
  1174.     int                 ready;
  1175.     Boolean             disp_contact = FALSE, serv_contact = FALSE;
  1176.     Uint4               this_req;
  1177.     fd_set              readfds;
  1178.     NI_DispatcherPtr    disp = req->dispatcher;
  1179.  
  1180.     ni_errtext[0] = '\0';
  1181.     if ((sconnhp = MsgMakeHandle(FALSE)) == NULL) {
  1182.         ni_errno = NIE_MAKEHAND;
  1183.         return NULL;
  1184.     }
  1185.  
  1186.     if (activityHook != NULL)
  1187.     {
  1188.         activityHook(NULL, NetServHook_svcreq, 0);
  1189.     }
  1190.  
  1191.     svcreqp = NI_MakeMsgSvcreq();
  1192.     svcreqp->seqno = disp->dispHP->seqno++;
  1193.     svcreqp->platform = (Uint4) NI_GetPlatform();
  1194.     if (stackDescription != NULL)
  1195.     {
  1196.         svcreqp->applId = StringSave(stackDescription);
  1197.     }
  1198.     this_req = svcreqp->seqno;
  1199.     CopyIdentity(disp, svcreqp->uid);
  1200.     NI_DestroyRequest(svcreqp->request);
  1201.     svcreqp->request = req;
  1202.     if ((mp = MsgBuild(NI_SVC_REQUEST, disp->dispHP->conid, (VoidPtr) svcreqp)) == NULL) {
  1203.         NI_DestroyRequest(req);
  1204.         MsgDestroyHandle(sconnhp);
  1205.         ni_errno = NIE_MISC;            /* unable to alloc mem for Msg */
  1206.         return NULL;
  1207.     }
  1208.  
  1209.     if (MsgWrite(disp->dispHP, mp) < 0) {
  1210.         MsgDestroyHandle(sconnhp);
  1211.         ni_errno = NIE_MSGWRITE;
  1212.         return NULL;
  1213.     }
  1214.  
  1215.     /* blocks until SVC_RESPONSE from dispatcher and service or NACK or TIMEOUT */
  1216.  
  1217.     while (!disp_contact || !serv_contact) {
  1218.         timeout.tv_sec = (Uint4) NI_TIMEOUT_SECS;
  1219.         timeout.tv_usec = 0;
  1220.         FD_ZERO(&readfds);
  1221.         FD_SET(disp->dispHP->sok, &readfds);
  1222.         FD_SET(disp->svcsHP->sok, &readfds);
  1223.         while ((ready = NI_select(FD_SETSIZE, &readfds, NULL, NULL, &timeout)) < 0) {
  1224.             if (SOCK_ERRNO == EINTR)
  1225.                 ;                                       /* repeat while interrupted */
  1226.             else {
  1227.                 MsgDestroyHandle(sconnhp);
  1228.                 ni_errno = NIE_SELECT;          /* select error */
  1229.                 return NULL;
  1230.             }
  1231.         }
  1232.         if (ready == 0) {
  1233.             MsgDestroyHandle(sconnhp);
  1234.             ni_errno = NIE_DSPTIMEOUT;          /* TIMEOUT */
  1235.             return NULL;
  1236.         }
  1237.         if (FD_ISSET(disp->dispHP->sok, &readfds) != 0) {
  1238.             if ((imp = MsgRead(disp->dispHP, FALSE)) == NULL) {
  1239.                 NI_CLOSESOCKET(disp->dispHP->sok);
  1240.                 MsgDestroyHandle(sconnhp);
  1241.                 ni_errno = NIE_MSGREAD;
  1242.                 return NULL;
  1243.             }
  1244.             disp_contact = TRUE;
  1245.             switch (imp->type) {
  1246.               case NI_SVC_RESPONSE:
  1247.                 ni_errno = NIE_NO_ERROR;
  1248.                 NI_DestroyRequest(disp->reqResponse);
  1249.                 disp->reqResponse = imp->msun.svcresp->request;
  1250.                 sconnhp->hostname = StringSave(disp->reqResponse->clientAddr);
  1251.                 imp->msun.svcresp->request = NULL;
  1252.                 MsgDestroy(imp);
  1253.                 break;
  1254.  
  1255.               case NI_NACK:
  1256.                 ni_errno = (enum ni_error) imp->msun.nack->code;
  1257.                 if (imp->msun.nack->reason != NULL)
  1258.                     StringCpy(ni_errtext, imp->msun.nack->reason);
  1259.                 else
  1260.                     ni_errtext[0] = '\0';
  1261.                 MsgDestroy(imp);
  1262.                 MsgDestroyHandle(sconnhp);
  1263.                 return NULL;
  1264.  
  1265.               default:
  1266.                 MsgDestroy(imp);
  1267.                 MsgDestroyHandle(sconnhp);
  1268.                 ni_errno = NIE_MSGUNK;          /* Unknown MSG type */
  1269.                 sprintf(ni_errtext, "%d", imp->type);
  1270.                 return NULL;
  1271.             }
  1272.         }
  1273.         if ((FD_ISSET(disp->svcsHP->sok, &readfds) != 0) && !serv_contact) {
  1274.             sconnlen = sizeof(sconnaddr);
  1275. #ifdef NETP_INET_NEWT
  1276.             sconnhp->sok = accept(disp->svcsHP->sok, &sconnaddr, &sconnlen);
  1277. #else
  1278.             sconnhp->sok = accept(disp->svcsHP->sok, (struct sockaddr PNTR) &sconnaddr, &sconnlen);
  1279. #endif /* NETP_INET_NEWT */
  1280.             if (sconnhp->sok < 0) {
  1281. #ifdef NETP_INET_NEWT
  1282.                 SOCK_ERRNO = ABS(sconnhp->sok);
  1283. #endif
  1284.                 MsgDestroyHandle(sconnhp);
  1285.                 StringCpy(ni_errtext, sys_errlist[SOCK_INDEX_ERRNO]);
  1286.                 ni_errno = NIE_NOACCEPT;        /* application accept error */
  1287.                 return NULL;
  1288.             }
  1289.             serv_contact = TRUE;
  1290.         }
  1291.     }
  1292.     return sconnhp;
  1293. } /* NI_ServiceRequest */
  1294.  
  1295.  
  1296.  
  1297. /*
  1298.  * Purpose:     Disconnect from a service provider
  1299.  *
  1300.  * Parameters:
  1301.  *   mhp          Message handle for the server
  1302.  *
  1303.  * Returns:
  1304.  *               0, always
  1305.  *
  1306.  *
  1307.  * Description:
  1308.  *              Disconnect from a service provider, essentially by just
  1309.  *              closing the communication socket to that service provider.
  1310.  */
  1311.  
  1312. Int2
  1313. NI_ServiceDisconnect(NI_HandPtr mhp)
  1314. {
  1315.     if (activityHook != NULL)
  1316.     {
  1317.         activityHook(mhp, NetServHook_svcdisconn, 0);
  1318.     }
  1319.  
  1320.     MsgDestroyHandle(mhp);
  1321.     return 0;
  1322. } /* NI_ServiceDisconnect */
  1323.  
  1324.  
  1325.  
  1326. /*
  1327.  * Purpose:     Obtain the read file descriptor from a "message handle"
  1328.  *
  1329.  * Parameters:
  1330.  *   handp        Message handle
  1331.  *
  1332.  * Returns:
  1333.  *               Socket associated with message handle
  1334.  *
  1335.  *
  1336.  * Description:
  1337.  *              Get the read file desciptor from a message handle. This
  1338.  *              might be useful, for example, when wishing to perform
  1339.  *              "direct" I/O to the socket after a connection has been
  1340.  *              established with a server/client.
  1341.  */
  1342.  
  1343. int
  1344. NI_ServiceGetReadFd(NI_HandPtr handp)
  1345. {
  1346.     return handp->sok;
  1347. } /* NI_ServiceGetReadFd */
  1348.  
  1349.  
  1350.  
  1351. /*
  1352.  * Purpose:     Obtain the write file descriptor from a "message handle"
  1353.  *
  1354.  * Parameters:
  1355.  *   handp        Message handle
  1356.  *
  1357.  * Returns:
  1358.  *               Socket associated with message handle
  1359.  *
  1360.  *
  1361.  * Description:
  1362.  *              Get the write file desciptor from a message handle. This
  1363.  *              might be useful, for example, when wishing to perform
  1364.  *              "direct" I/O to the socket after a connection has been
  1365.  *              established with a server/client.
  1366.  */
  1367.  
  1368. int
  1369. NI_ServiceGetWriteFd(NI_HandPtr handp)
  1370. {
  1371.     return handp->sok;
  1372. } /* NI_ServiceGetWriteFd */
  1373.  
  1374.  
  1375.  
  1376. /*
  1377.  * Purpose:     Populate a service request with a service name and version #s
  1378.  *
  1379.  * Parameters:
  1380.  *   req          Service request
  1381.  *   name         Service name
  1382.  *   vermin       Minimum version number for this service
  1383.  *   vermax       Maximum version number for this service
  1384.  *
  1385.  * Returns:
  1386.  *               -1, if the name is a NULL pointer
  1387.  *               0, otherwise
  1388.  *
  1389.  *
  1390.  * Description:
  1391.  *              Populate the service request with the specified service name
  1392.  *              and version numbers, dynamically allocating space for the
  1393.  *              service name.
  1394.  */
  1395.  
  1396. Int2
  1397. NI_RequestSetService(NI_ReqPtr req, CharPtr name, Uint2 vermin, Uint2 vermax)
  1398. {
  1399.     if (name == NULL) {
  1400.         ni_errno = NIE_INVAL;
  1401.         return -1;
  1402.     }
  1403.     req->service->name = StringSave(name);
  1404.     req->service->minVersion = vermin;
  1405.     req->service->maxVersion = vermax;
  1406.     req->service->typeL = NULL;
  1407.     return 0;
  1408. } /* NI_RequestSetService */
  1409.  
  1410.  
  1411.  
  1412. /*
  1413.  * Purpose:     Populate a service request with an additional resource
  1414.  *
  1415.  * Parameters:
  1416.  *   req          Service request
  1417.  *   name         Resource name
  1418.  *   type         Service type
  1419.  *   vermin       Minimum version number for this resource
  1420.  *   vermax       Maximum version number for this resource
  1421.  *
  1422.  * Returns:
  1423.  *               -1, if the name is a NULL pointer
  1424.  *               0, otherwise
  1425.  *
  1426.  *
  1427.  * Description:
  1428.  *              Insert the information for this resource into a list of
  1429.  *              resources associated with this service request. This
  1430.  *              function may be called one or more times (or, not at all) to
  1431.  *              populate a service request with one or more resources.
  1432.  */
  1433.  
  1434. Int2
  1435. NI_RequestAddResource(NI_ReqPtr req, CharPtr name, CharPtr type, Uint2 vermin, Uint2 vermax)
  1436.  
  1437. {
  1438.     NIResPtr    resp;
  1439.  
  1440.     if (name == NULL) {
  1441.         ni_errno = NIE_INVAL;
  1442.         return -1;
  1443.     }
  1444.     resp = NI_MakeResource();
  1445.     resp->name = StringSave(name);
  1446.     if (type != NULL)
  1447.         resp->type = StringSave(type);
  1448.     resp->minVersion = vermin;
  1449.     resp->maxVersion = vermax;
  1450.     req->resourceL = ListInsertPrev((VoidPtr) resp, req->resourceL);    /* add to end of list */
  1451.     return 0;
  1452. } /* NI_RequestAddResource */
  1453.  
  1454.  
  1455.  
  1456. /* THESE FUNCTIONS NOT VISIBLE TO API USER */
  1457.  
  1458. /*
  1459.  * Purpose:     Partially halt Network Services
  1460.  *
  1461.  * Parameters:
  1462.  *   disp         A pointer to the dispatcher structure
  1463.  *
  1464.  * Description:
  1465.  *              Halt network services, except refrain from freeing the
  1466.  *              parameters which are set by NI_SetDispatcher().
  1467.  */
  1468.  
  1469. static void
  1470. HaltServices (NI_DispatcherPtr disp)
  1471. {
  1472.     if (disp == NULL)
  1473.         return;
  1474.  
  1475.     if (disp->referenceCount > 0)
  1476.         return;
  1477.  
  1478.     if (activityHook != NULL)
  1479.     {
  1480.         activityHook((NI_HandPtr) disp, NetServHook_dispdisconn, 0);
  1481.     }
  1482.  
  1483.     MsgDestroyHandle(disp->dispHP);
  1484.     MsgDestroyHandle(disp->svcsHP);
  1485.     NI_DestroyRequest(disp->reqResponse);
  1486.     if (disp->identity != NULL) {
  1487.         MemFree (disp->identity->username);
  1488.         MemFree (disp->identity->group);
  1489.         MemFree (disp->identity->domain);
  1490.         MemFree (disp->identity);
  1491.         disp->identity = NULL;
  1492.     }
  1493.     disp->dispHP = NULL;
  1494.     disp->svcsHP = NULL;
  1495.     disp->reqResponse = NULL;
  1496.  
  1497. #ifdef NETP_INET_WSOCK
  1498.     /* we have an obligation to perform one cleanup call for every Startup */
  1499.     while (wsaStartupCount-- > 0)
  1500.     {
  1501.         WSACleanup();
  1502.     }
  1503. #endif
  1504. }
  1505.  
  1506.  
  1507. /*
  1508.  * Purpose:     Lookup a port # in config file and possible NIS
  1509.  *
  1510.  * Parameters:
  1511.  *   service      Name of config. file entry
  1512.  *   networkOrder Boolean, indicates whether value should be returned in host
  1513.  *                order or network order.
  1514.  *
  1515.  * Description:
  1516.  *              Look up the specified entry in the NCBI config. file, and
  1517.  *              lookup in NIS the name obtained from the config file if it's
  1518.  *              non-numeric.
  1519.  *
  1520.  * Note:
  1521.  *              The intent of this function is that, in most cases, the
  1522.  *              GetAppParam() entry will not be present, and a default value
  1523.  *              will be used instead.  The getservbyname() call is intended
  1524.  *              to be a last resort, because this may be slow on some systems.
  1525.  */
  1526.  
  1527. static Uint2
  1528. GetByConfigOrServ(CharPtr service, Boolean networkOrder)
  1529. {
  1530.     struct servent PNTR portEntry;
  1531.     Char                buf[50];
  1532.     Uint2               port;
  1533.  
  1534.     if (GetAppParam("NCBI", "NET_SERV", service, "", buf, sizeof buf) <= 0)
  1535.     {
  1536.         port = 0;
  1537.     } else {
  1538.         if (StrSpn(buf, "0123456789") == StrLen(buf))
  1539.         { /* all numeric */
  1540.             port = atoi(buf);
  1541.             if (networkOrder)
  1542.                port = htons(port);
  1543.         } else {
  1544.             /* entry from configuration file is name to use in getservbyname */
  1545.             if ((portEntry = getservbyname(buf, "tcp")) == NULL)
  1546.             {
  1547.                 port = 0;
  1548.             } else  {
  1549.                 port = portEntry->s_port;
  1550.                 if (! networkOrder)
  1551.                     port = ntohs(port);
  1552.             }
  1553.         }
  1554.     }
  1555.  
  1556.     return port;
  1557. }
  1558.  
  1559.  
  1560. /*
  1561.  * Purpose:     Connect to the dispatcher
  1562.  *
  1563.  * Parameters:
  1564.  *   disp         A pointer to the dispatcher structure
  1565.  *   host         Name of the host on which dispatcher resides
  1566.  *   service      Name of the "service" (i.e., port) to which we should connect
  1567.  *   timeout      How long to wait for dispatcher to respond, 0 ==> use default
  1568.  *
  1569.  * Returns:
  1570.  *               NULL, if the attempt to connect failed
  1571.  *               a pointer to the "Msg" structure for the dispatcher, otherwise
  1572.  *
  1573.  *
  1574.  * Description:
  1575.  *               Connect to the dispatcher on the specified hostname on the
  1576.  *               specified service (where a service maps to a port number).
  1577.  *               This is done by establishing a socket to the dispatcher,
  1578.  *               and then connect()ing to that socket; the dispatcher should
  1579.  *               be listen()ing on that socket, and should subsequently accept()
  1580.  *               the connection request.
  1581.  *
  1582.  *               While doing this, also obtain other useful information;
  1583.  *               namely, the dotted IP address of the local host, and the
  1584.  *               high and low port numbers to be used when attempting
  1585.  *               dispatcher connections. This global information is used
  1586.  *               elsewhere.
  1587.  */
  1588.  
  1589. #ifndef INADDR_NONE
  1590. #define INADDR_NONE             -1
  1591. #endif /* INADDR_NONE */
  1592.  
  1593. static NI_HandPtr
  1594. DispatchConnect(NI_DispatcherPtr disp, CharPtr host, CharPtr service, int timeout)
  1595. {
  1596.     struct hostent      PNTR dispHost, PNTR localHost;
  1597.     struct sockaddr_in  serv_addr;
  1598.     NI_HandPtr          dHP;
  1599.     Uint2               disp_port;
  1600.     Uint4               srvadd;
  1601.     Char                servInetAddr[INETADDR_SIZ], localHostName[SVC_HOST_SIZ];
  1602.     Char                t_service[64];
  1603.     int                 status;
  1604.  
  1605.     if (disp == NULL)
  1606.         return NULL;
  1607.  
  1608.  
  1609.     serv_addr.sin_family = AF_INET;
  1610.  
  1611.     srvadd = inet_addr(host);
  1612.     if ((Int4)srvadd != INADDR_NONE)    /* malformed request */
  1613.         MemCopy((VoidPtr) &serv_addr.sin_addr, (VoidPtr) &srvadd, sizeof(srvadd));
  1614.     else {
  1615.         if ((dispHost = gethostbyname(host)) == NULL) {
  1616.             ni_errno = NIE_NOHOSTENT;
  1617.             return NULL;
  1618.         }
  1619. /*      MemCopy((VoidPtr)&serv_addr.sin_addr, (VoidPtr)(dispHost->h_addr), dispHost->h_length);*/
  1620.         MemCopy(&serv_addr.sin_addr, dispHost->h_addr, dispHost->h_length);
  1621.     }
  1622.     StringCpy(servInetAddr, inet_ntoa(serv_addr.SIN_ADDR));
  1623.  
  1624.     if ((disp_port = GetByConfigOrServ(service, TRUE)) == 0)
  1625.     {
  1626.         if (service)
  1627.             StringCpy(t_service, service);      /* because Windows barfs on the pointer */
  1628.         else
  1629.             t_service[0] = 0;
  1630.         if ((disp_port = htons(atoi(t_service))) == 0)
  1631.             disp_port = htons(NI_DFLT_SVC_PORT);
  1632.     }
  1633.     if (ntohs(disp_port) <= NI_LAST_RESERVED_PORT) {
  1634.         ni_errno = NIE_NOSERVENT;
  1635.         return NULL;
  1636.     }
  1637.  
  1638.     /* get the Internet address of the "local host" */
  1639. #ifdef OS_MAC
  1640.     /* simpler solution to avoid the hazards of gethostname() */
  1641.     {
  1642.         unsigned long localHostId;
  1643.  
  1644.         localHostId = gethostid();
  1645.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) &localHostId));
  1646.     }
  1647. #else
  1648.     gethostname(localHostName, SVC_HOST_SIZ);
  1649.     if ((localHost = gethostbyname(localHostName)) == NULL) {
  1650.         /* GetAppParam() workaround for PC-NFS 5.0 bug */
  1651.         if (GetAppParam("NCBI", "NET_SERV", "HOST_ADDRESS", "",
  1652.                         disp->localHostAddr, sizeof(disp->localHostAddr)) <= 0)
  1653.         { /* use a bogus address which the dispatcher will try to fix */
  1654.             StringCpy(disp->localHostAddr, "0.0.0.0");
  1655.  
  1656.         }
  1657.     } else {
  1658.         StringCpy(disp->localHostAddr, inet_ntoa(* (H_ADDR_TYPE) localHost->h_addr));
  1659.     }
  1660. #endif /* OS_MAC */
  1661.  
  1662.     if ((disp->loport = GetByConfigOrServ(NI_CLIENT_PORT_LO_NAME, FALSE)) == 0)
  1663.     {
  1664.         if ((disp->loport = atoi(NI_CLIENT_PORT_LO_NAME)) == 0)
  1665.             disp->loport = NI_DFLT_CLILO_PORT;
  1666.     }
  1667.     if (disp->loport <= NI_LAST_RESERVED_PORT) {
  1668.         ni_errno = NIE_BADPORT;         /* bad low client port */
  1669.         return NULL;
  1670.     }
  1671.  
  1672.     if ((disp->hiport = GetByConfigOrServ(NI_CLIENT_PORT_HI_NAME, FALSE)) == 0)
  1673.     {
  1674.         if ((disp->hiport = atoi(NI_CLIENT_PORT_HI_NAME)) == 0)
  1675.             disp->hiport = NI_DFLT_CLIHI_PORT;
  1676.     }
  1677.     if (disp->hiport <= NI_LAST_RESERVED_PORT) {
  1678.         ni_errno = NIE_BADPORT;         /* bad high client port */
  1679.         return NULL;
  1680.     }
  1681.  
  1682.     MemFill((VoidPtr) &serv_addr, '\0', sizeof(serv_addr));
  1683.     serv_addr.sin_family = AF_INET;
  1684.     serv_addr.sin_addr.s_addr = inet_addr(servInetAddr);
  1685.     serv_addr.sin_port = disp_port;
  1686.  
  1687.     if ((dHP = MsgMakeHandle(TRUE)) == NULL)
  1688.         return NULL;
  1689.     MsgSetLJError(dHP);
  1690.     if (timeout > 0)
  1691.         MsgSetReadTimeout(dHP, timeout);
  1692.  
  1693.     if (activityHook != NULL)
  1694.     {
  1695.         activityHook((NI_HandPtr) disp, NetServHook_dispconn, 0);
  1696.     }
  1697.  
  1698.   RETRY:
  1699. #ifndef NETP_INET_NEWT
  1700.     if ((status = connect(dHP->sok, (struct sockaddr PNTR) &serv_addr, sizeof(serv_addr))) < 0) { /* } */
  1701. #else
  1702.     if ((status = connect(dHP->sok, &serv_addr, sizeof(serv_addr))) < 0) {
  1703.         SOCK_ERRNO = ABS(status);
  1704. #endif
  1705.         switch (SOCK_ERRNO) {
  1706.           case EINTR:
  1707.             goto RETRY;
  1708.  
  1709. #ifdef NETP_INET_PCNFS
  1710.           /* This is apparently a bug in PC-NFS 4.0 ... a connection attempt */
  1711.           /* on a non-blocking socket yields errno == 0                      */
  1712.           case 0:
  1713. #endif /* NETP_INET_PCNFS */
  1714.           case EWOULDBLOCK:
  1715.           case EINPROGRESS:
  1716.             /* if the connect()ion is not established immediately, a         */
  1717.             /* select() can be performed where the corresponding "write"     */
  1718.             /* file descriptor will be enabled once the connect()ion has been*/
  1719.             /* established                                                   */
  1720.             if (sokselectw(dHP->sok, timeout) == 0) {
  1721.                 dHP->state = NI_CONNECTED;
  1722.                 return dHP;
  1723.             }
  1724.             break;
  1725.  
  1726.           default:
  1727.             break;
  1728.         }
  1729.         MsgDestroyHandle(dHP);
  1730.         ni_errno = NIE_DISPCONN;        /* can't connect to dispatcher */
  1731.         return NULL;
  1732.     }
  1733.     dHP->state = NI_CONNECTED;
  1734.     return dHP;
  1735. } /* DispatchConnect */
  1736.  
  1737.  
  1738. /*
  1739.  * Purpose:     Get the platform on which this client is running
  1740.  *
  1741.  * Parameters:
  1742.  *                none
  1743.  *
  1744.  * Returns:
  1745.  *               the client's platform, or NI_PLATFORM_UNKNOWN
  1746.  *
  1747.  *
  1748.  * Description:
  1749.  *               Calculate what platform this client is running on.
  1750.  *
  1751.  *
  1752.  * Note:
  1753.  *               Although the initial implementation of this function
  1754.  *               calculates the platform-type at compile-time, it is
  1755.  *               legitimate to perform some computation at run time, e.g.,
  1756.  *               to determine whether this client is using a particular
  1757.  *               low-level driver.
  1758.  *
  1759.  *               The dispatcher and servers should not rely on the
  1760.  *               information which is received for platform-type, because
  1761.  *               the client may be lying, either because of a coding error
  1762.  *               or malice on the part of a client developer.
  1763.  */
  1764.  
  1765. Int2
  1766. NI_GetPlatform (void)
  1767. {
  1768.     static Boolean alreadyInited = FALSE;
  1769.     static Int2 retval;
  1770.  
  1771.     if (alreadyInited)
  1772.     {
  1773.       return retval;
  1774.     }
  1775.  
  1776.     alreadyInited = TRUE;
  1777.  
  1778.     retval = NI_PLATFORM_UNKNOWN;
  1779.  
  1780. #ifdef OS_MAC
  1781.     retval = NI_PLATFORM_MAC;
  1782. #endif
  1783.  
  1784. #ifdef OS_VMS
  1785. #ifdef NETP_INET_TGV
  1786.     retval = NI_PLATFORM_VMS_TGV;
  1787. #endif
  1788. #ifdef NETP_INET_TWG
  1789.     retval = NI_PLATFORM_VMS_TWG;
  1790. #endif
  1791. #ifdef NETP_INET_UCX
  1792.     retval = NI_PLATFORM_VMS_UCX;
  1793. #endif
  1794. #ifdef OS_AXP_VMS
  1795.     retval = NI_PLATFORM_AXP_OPENVMS;
  1796. #endif
  1797. #endif /* OS_VMS */
  1798.  
  1799. #ifdef OS_UNIX
  1800.     retval = NI_PLATFORM_GENERIC_UNIX;
  1801. #ifdef PROC_IBM370
  1802.     retval = NI_PLATFORM_IBM370AIX;
  1803. #endif
  1804. #ifdef OS_UNIX_SUN
  1805.     retval = NI_PLATFORM_SUN;
  1806. #endif
  1807. #if defined(OS_UNIX_OSF1) && defined(PROC_ALPHA)
  1808.     retval = NI_PLATFORM_ALPHA_OSF1;
  1809. #endif
  1810. #ifdef COMP_AUX
  1811.     retval = NI_PLATFORM_AUX;
  1812. #endif
  1813. #if defined(COMP_CRAY) && defined(PROC_YMP)
  1814.     retval = NI_PLATFORM_CRAY;
  1815. #endif
  1816. #ifdef PROC_CONVEX
  1817.     retval = NI_PLATFORM_CONVEX;
  1818. #endif
  1819. #ifdef PROC_HPPA
  1820.     retval = NI_PLATFORM_HPUX;
  1821. #endif
  1822. #ifdef OS_UNIX_NEXT
  1823.     retval = NI_PLATFORM_NEXT;
  1824. #endif
  1825. #ifdef PROC_MIPS
  1826.     retval = NI_PLATFORM_SGI;
  1827. #endif
  1828. #ifdef OS_UNIX_ULTRIX
  1829.     retval = NI_PLATFORM_ULTRIX;
  1830. #endif
  1831. #if defined(OS_UNIX_SYSV) && defined(PROC_SPARC)
  1832.     retval = NI_PLATFORM_SYSV_ON_SPARC;
  1833. #endif
  1834. #endif /* OS_UNIX */
  1835.  
  1836. #ifdef OS_DOS
  1837.     retval = NI_PLATFORM_DOS;
  1838. #ifdef WIN16
  1839.     retval = NI_PLATFORM_WIN16;
  1840. #endif
  1841. #ifdef NETP_INET_NEWT
  1842.     retval = NI_PLATFORM_WIN_NEWT;
  1843. #endif
  1844. #ifdef NETP_INET_PCNFS
  1845.     retval = NI_PLATFORM_WIN_PCNFS;
  1846. #endif
  1847. #ifdef WINSOCK
  1848.     retval = NI_PLATFORM_WIN_WINSOCK;
  1849. #endif
  1850. #endif /* OS_DOS */
  1851.  
  1852. #ifdef OS_WINNT
  1853.     retval = NI_PLATFORM_WINNT;
  1854. #endif
  1855.  
  1856.     return retval;
  1857. }
  1858.  
  1859.  
  1860. /*
  1861.  * Purpose:     Set the "identity" of this client
  1862.  *
  1863.  * Parameters:
  1864.  *   disp         A pointer to the dispatcher structure
  1865.  *   user         New Username
  1866.  *   group        New Groupname
  1867.  *   domain       New DomainName
  1868.  *
  1869.  * Returns:
  1870.  *               0, always
  1871.  *
  1872.  *
  1873.  * Description:
  1874.  *               Allocate the space for the "UID" structure, if not already
  1875.  *               allocated, and populate it with the user name, group name,
  1876.  *               and domain name.
  1877.  */
  1878.  
  1879. static Int2
  1880. SetIdentity(NI_DispatcherPtr disp, CharPtr user, CharPtr group, CharPtr domain)
  1881. {
  1882.     if (disp == NULL)
  1883.         return 0;
  1884.  
  1885.     if (disp->identity == NULL)
  1886.         disp->identity = NI_MakeUid();
  1887.  
  1888.     if (disp->identity->username != NULL)
  1889.         MemFree(disp->identity->username);
  1890.     disp->identity->username = StringSave(user);
  1891.     if (disp->identity->group != NULL)
  1892.         MemFree(disp->identity->group);
  1893.     if (group != NULL)
  1894.         disp->identity->group = StringSave(group);
  1895.     else
  1896.         disp->identity->group = NULL;
  1897.     if (disp->identity->domain != NULL)
  1898.         MemFree(disp->identity->domain);
  1899.     disp->identity->domain = StringSave(domain);
  1900.     return 0;
  1901. } /* SetIdentity */
  1902.  
  1903.  
  1904.  
  1905. /*
  1906.  * Purpose:     Copy from the "identity" UID to the specified UID data struct
  1907.  *
  1908.  * Parameters:
  1909.  *   disp         A pointer to the dispatcher structure
  1910.  *   uid          UID structure to be copied into
  1911.  *
  1912.  * Returns:
  1913.  *               -1, if invalid arguments
  1914.  *               0, otherwise
  1915.  *
  1916.  *
  1917.  * Description:
  1918.  *              Copy fields from the "identity" UID data structure into the
  1919.  *              UID data structure provided by the caller.
  1920.  */
  1921.  
  1922. static Int2
  1923. CopyIdentity(NI_DispatcherPtr disp, NI_UidPtr uid)
  1924. {
  1925.     if (disp == NULL || disp->identity == NULL || uid == NULL)
  1926.         return -1;
  1927.     if  (uid->username != NULL)
  1928.         MemFree(uid->username);
  1929.     uid->username = StringSave(disp->identity->username);
  1930.     if  (uid->group != NULL)
  1931.         MemFree(uid->group);
  1932.     uid->group = StringSave(disp->identity->group);
  1933.     if  (uid->domain != NULL)
  1934.         MemFree(uid->domain);
  1935.     uid->domain = StringSave(disp->identity->domain);
  1936.     return 0;
  1937. } /* CopyIdentity */
  1938.  
  1939.  
  1940.  
  1941. /*
  1942.  * Purpose:     Select the next available port within the given range,
  1943.  *              and bind a socket to it.
  1944.  *
  1945.  * Parameters:
  1946.  *   sok          Socket to be bound to a port (INPUT)
  1947.  *   sokadr       Socket data structure to be populated (OUTPUT)
  1948.  *   loport       Minimum acceptable port number
  1949.  *   hiport       Maximum acceptable port number
  1950.  *
  1951.  * Returns:
  1952.  *               0, if unable to bind to a port
  1953.  *               the selected ("bound") port number, otherwise
  1954.  *
  1955.  *
  1956.  * Description:
  1957.  *              Iterate through the range of acceptable port numbers, until
  1958.  *              an unused port number can be selected to which the socket
  1959.  *              can be bound.
  1960.  */
  1961.  
  1962. static Uint2
  1963. bindPort(int sok, struct sockaddr_in PNTR sokadr, Int2 loport, Int2 hiport)
  1964. {
  1965.     int                 status;
  1966. #ifdef OS_MAC
  1967.     int                 delta = 0;
  1968.     Char                buf[20];
  1969. #endif
  1970.  
  1971.     if (hiport == 0)
  1972.         hiport = loport;
  1973.     if (loport > hiport)
  1974.         return 0;
  1975.  
  1976. #ifdef OS_MAC
  1977.     /* use a hint from the configuration file to avoid port # conflicts */
  1978.     if (hiport > loport && GetAppParam("NCBI", "NET_SERV", "PORT_DELTA", "0",
  1979.         buf, sizeof buf) > 0)
  1980.     {
  1981.         delta = atoi(buf);
  1982.         loport += delta % (hiport - loport);
  1983.         sprintf (buf, "%d", delta + 1);
  1984.         SetAppParam("NCBI", "NET_SERV", "PORT_DELTA", buf);
  1985.     }
  1986. #endif
  1987.  
  1988.     MemFill((VoidPtr) sokadr, '\0', sizeof(struct sockaddr_in));
  1989.     sokadr->sin_family = AF_INET;
  1990.     sokadr->sin_addr.s_addr = INADDR_ANY;
  1991.  
  1992.     while (loport <= hiport) {
  1993.         sokadr->sin_port = htons(loport);
  1994. #ifdef NETP_INET_NEWT
  1995.         if ((status = bind(sok, sokadr, sizeof(struct sockaddr_in))) == 0)
  1996. #else
  1997.         if ((status = bind(sok, (struct sockaddr PNTR) sokadr, sizeof(struct sockaddr_in))) == 0)
  1998. #endif /* NETP_INET_NEWT */
  1999.             return (Uint2) ntohs(sokadr->sin_port);
  2000.         else {
  2001. #ifdef NETP_INET_NEWT
  2002.             SOCK_ERRNO = ABS(status);
  2003. #endif
  2004.             loport++;
  2005.         }
  2006.     }
  2007.     return 0;
  2008. } /* bindPort */
  2009.  
  2010.  
  2011.  
  2012. /* SERVER FUNCTIONS */
  2013.  
  2014. static int      writepipe PROTO((int fd, char *buf, int len));
  2015.  
  2016. /*
  2017.  * Purpose:     Write a message on the pipe from a child server application
  2018.  *              process to its parent NCBID.
  2019.  *
  2020.  * Parameters:
  2021.  *   fd           Pipe file descriptor
  2022.  *   buf          Buffer to be written
  2023.  *   len          Length of buffer
  2024.  *
  2025.  * Returns:
  2026.  *                0, if unable to write because the pipe is full
  2027.  *                number of bytes written, otherwise
  2028.  *
  2029.  *
  2030.  * Description:
  2031.  *              Write the specified number of bytes to a pipe, and handle
  2032.  *              multiple write attempts if necessary, to handle the case where
  2033.  *              a write() may be interrupted by a signal.
  2034.  *
  2035.  * Note:
  2036.  *              This routine is only used by a child process after it has been
  2037.  *              forked and before it has been execed.
  2038.  */
  2039.  
  2040. static int
  2041. writepipe(int fd, char *buf, int len)
  2042. {
  2043.     int         byteswrit;
  2044.  
  2045.   WriteAgain:
  2046.     if ((byteswrit = write(fd, buf, len)) < 0) {
  2047.         switch (errno) {
  2048.           case EINTR:
  2049.             goto WriteAgain;
  2050.  
  2051.           case EWOULDBLOCK:
  2052.           default:
  2053.             return 0;
  2054.         }
  2055.     }
  2056.  
  2057.     return byteswrit;
  2058. } /* writepipe */
  2059.  
  2060.  
  2061.  
  2062. /*
  2063.  * Purpose:     Send an "ACK" from a child server application process to its
  2064.  *              parent NCBID.
  2065.  *
  2066.  * Returns:
  2067.  *                0, if the ACK was sent successfully
  2068.  *                -1, otherwise
  2069.  *
  2070.  *
  2071.  * Description:
  2072.  *              Write an "ACK" from a child server application process to its
  2073.  *              parent NCBID, on the pipe connecting the two processes.
  2074.  *
  2075.  * Note:
  2076.  *              This routine should be called by a child process after it has
  2077.  *              determined that it has started successfully. At most one
  2078.  *              of NI_ServerACK() and NI_ServerNACK() may be called.
  2079.  */
  2080.  
  2081. #define TEMP_BUF_SIZ    256
  2082.  
  2083. int
  2084. NI_ServerACK(void)
  2085. {
  2086.     int         wstat;
  2087.     Char        temp_buf[TEMP_BUF_SIZ];
  2088.  
  2089.     sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVACK, "OK");
  2090.     if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  2091.         ni_errno = NIE_PIPEIO;
  2092.         strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  2093.         return -1;
  2094.     }
  2095.     return 0;
  2096. } /* NI_ServerACK */
  2097.  
  2098.  
  2099.  
  2100. /*
  2101.  * Purpose:     Send an "NACK" from a child server application process to its
  2102.  *              parent NCBID.
  2103.  *
  2104.  * Returns:
  2105.  *                0, if the NACK was sent successfully
  2106.  *                -1, otherwise
  2107.  *
  2108.  *
  2109.  * Description:
  2110.  *              Write an "NACK" from a child server application process to its
  2111.  *              parent NCBID, on the pipe connecting the two processes.
  2112.  *
  2113.  * Note:
  2114.  *              This routine should be called by a child process after it has
  2115.  *              determined that it will be unable to start successfully. In
  2116.  *              the event that this routine is not called (or is unable to
  2117.  *              perform its function), a timeout mechanism must be relied
  2118.  *              upon for the NCBID to realize that a child has started
  2119.  *              unsuccessfully.
  2120.  *
  2121.  *              At most one of NI_ServerACK() and NI_ServerNACK() may be called.
  2122.  */
  2123.  
  2124. int
  2125. NI_ServerNACK(CharPtr err_text)
  2126. {
  2127.     int         wstat;
  2128.     Char        temp_buf[TEMP_BUF_SIZ];
  2129.  
  2130.     sprintf(temp_buf, PIPE_MSG_FMT, NIE_SERVNACK, err_text);
  2131.     if ((wstat = writepipe(STDPIPE, temp_buf, strlen(temp_buf))) <= 0) {
  2132.         ni_errno = NIE_PIPEIO;
  2133.         strcpy(ni_errtext, (wstat == 0) ? "EWOULDBLOCK" : sys_errlist[errno]);
  2134.         return -1;
  2135.     }
  2136.     return 0;
  2137. } /* NI_ServerNACK */
  2138.  
  2139.  
  2140.  
  2141. /*
  2142.  * Purpose:     Open the stream to be used for ASN I/O between a server
  2143.  *              application process and its client.
  2144.  *
  2145.  * Returns:
  2146.  *                NULL, if something went wrong
  2147.  *                a pointer to the Msg structure, otherwise
  2148.  *
  2149.  *
  2150.  * Description:
  2151.  *              Create a "Msg" structure for ASN I/O, and associate the Msg's
  2152.  *              socket with the standard input file descriptor (STDIN), which is
  2153.  *              the communication socket between the server application process
  2154.  *              and its client.
  2155.  *
  2156.  * Note:
  2157.  *              This routine should only be called by a child application
  2158.  *              process (not by a client).
  2159.  */
  2160.  
  2161. NI_HandPtr
  2162. NI_OpenASNIO(void)
  2163. {
  2164.     NI_HandPtr  hp;
  2165.  
  2166.     if ((hp = MsgMakeHandle(FALSE)) == NULL)
  2167.         return NULL;
  2168.  
  2169.     MsgSetReadTimeout(hp, NI_SERV_LISTEN_TIMEOUT);      /* set default for servers to listen */
  2170.  
  2171.     if ((hp->sok = dup(STDIN)) == -1) {         /* STDOUT points to same socket */
  2172.         MsgDestroyHandle(hp);
  2173.         return NULL;
  2174.     }
  2175.     return hp;
  2176. } /* NI_OpenASNIO */
  2177.  
  2178.  
  2179.  
  2180. /*
  2181.  * Purpose:     Close the ASN stream between a server application process and
  2182.  *              its client.
  2183.  *
  2184.  * Returns:
  2185.  *                -1 if something went wrong
  2186.  *                0, otherwise
  2187.  *
  2188.  *
  2189.  * Description:
  2190.  *              Close the stream by closing the socket and deleting the
  2191.  *              associated data structures.
  2192.  *
  2193.  * Note:
  2194.  *              This routine should only be called by a child application
  2195.  *              process (not by a client).
  2196.  */
  2197.  
  2198. Int2
  2199. NI_CloseASNIO(NI_HandPtr hp)
  2200. {
  2201.     return MsgDestroyHandle(hp);
  2202. } /* NI_CloseANSIO */
  2203.  
  2204.  
  2205.  
  2206. /* MISC FUNCTIONS */
  2207.  
  2208. /* sokselectr and sokselectw are not prototyped in ni_lib.h */
  2209.  
  2210. /*
  2211.  * Purpose:     Wait for a "read" socket to become ready to read, or for
  2212.  *              a timeout to occur.
  2213.  *
  2214.  * Returns:
  2215.  *                -1 if something went wrong
  2216.  *                0, otherwise
  2217.  *
  2218.  *
  2219.  * Description:
  2220.  *              Wait for the indicated "read" socket to be marked as
  2221.  *              "selected" by a socket() call.
  2222.  *
  2223.  * Note:
  2224.  *              This routine is presently unused.
  2225.  *
  2226.  *              The timeout mechanism is not exactly enforced, because
  2227.  *              received signals could result in a longer timeout period.
  2228.  */
  2229.  
  2230. int
  2231. sokselectr(int fd)
  2232. {
  2233.     fd_set      rfds;
  2234.     int         ready;
  2235.     struct timeval      timeout;
  2236.  
  2237.     FD_ZERO(&rfds);
  2238.     FD_SET(fd, &rfds);
  2239.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2240.     timeout.tv_usec = 0;
  2241.     while ((ready = select(fd+1, &rfds, NULL, NULL, &timeout)) == -1) {
  2242.         switch (SOCK_ERRNO) {
  2243.           case EINTR:
  2244.             continue;
  2245.  
  2246.           default:
  2247.             ni_errno = NIE_MISC;
  2248.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  2249.             return -1;
  2250.         }
  2251.     }
  2252.     if (ready == 0) {
  2253.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  2254.         ni_errno = NIE_TIMEOUT;
  2255.         return -1;
  2256.     }
  2257.     if (FD_ISSET(fd, &rfds))
  2258.         return 0;
  2259.     else
  2260.         return -1;
  2261. } /* sokselectr */
  2262.  
  2263.  
  2264.  
  2265. /*
  2266.  * Purpose:     Wait for a "write" socket to become ready to write, or for
  2267.  *              a timeout to occur.
  2268.  *
  2269.  * Returns:
  2270.  *                -1 if something went wrong
  2271.  *                0, otherwise
  2272.  *
  2273.  *
  2274.  * Description:
  2275.  *              Wait for the indicated "write" socket to be marked as
  2276.  *              "selected" by a socket() call.
  2277.  *
  2278.  * Note:
  2279.  *              This routine can be used when waiting for a connect() to go
  2280.  *              through successfully.
  2281.  *
  2282.  *              The timeout mechanism is not exactly enforced, because
  2283.  *              received signals could result in a longer timeout period.
  2284.  */
  2285.  
  2286. int
  2287. sokselectw(int fd, int seconds)
  2288. {
  2289.     fd_set      wfds;
  2290.     int         ready;
  2291.     struct timeval      timeout;
  2292.  
  2293.     FD_ZERO(&wfds);
  2294.     FD_SET(fd, &wfds);
  2295.     timeout.tv_sec = NI_SELECT_TIMEOUT;
  2296.     if (seconds > 0) /* override default */
  2297.         timeout.tv_sec = seconds;
  2298.     timeout.tv_usec = 0;
  2299.     while ((ready = select(fd+1, NULL, &wfds, NULL, &timeout)) == -1) {
  2300.         switch (SOCK_ERRNO) {
  2301.           case EINTR:
  2302.             continue;
  2303.  
  2304.           default:
  2305.             ni_errno = NIE_MISC;
  2306.             sprintf(ni_errtext, "%s", sys_errlist[SOCK_INDEX_ERRNO]);
  2307.             return -1;
  2308.         }
  2309.     }
  2310.     if (ready == 0) {
  2311.         strcpy(ni_errtext, ni_errlist[ni_errno]);
  2312.         ni_errno = NIE_TIMEOUT;
  2313.         return -1;
  2314.     }
  2315.     if (FD_ISSET(fd, &wfds))
  2316.     {
  2317. #ifdef OS_UNIX
  2318.         int err;
  2319.         int optlen;
  2320.  
  2321.         optlen = sizeof(int);
  2322.         if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (char *) &err, &optlen) >= 0 &&
  2323.             err != 0) /* check for an error */
  2324.             return -1; /* got some error */
  2325. #endif /* OS_UNIX */
  2326.         return 0;
  2327.     }
  2328.     else
  2329.         return -1;
  2330. } /* sokselectw */
  2331.  
  2332.  
  2333.  
  2334. /*
  2335.  * Purpose:     Parse the error number from an ASN error string which was
  2336.  *              formatted at a low level.
  2337.  *
  2338.  * Returns:
  2339.  *                -1, if unable to parse the string
  2340.  *                the parsed error number, otherwise
  2341.  *
  2342.  *
  2343.  * Description:
  2344.  *              Parse the error number from an ASN error string which was
  2345.  *              prepared by the ASN tools, and formatted at a low level.
  2346.  *
  2347.  * Note:
  2348.  *              The parsing mechanism is dependent upon any future format
  2349.  *              changes which may occur in the ASN tools.
  2350.  */
  2351.  
  2352. int
  2353. getAsnError(char *str)
  2354. {
  2355.     int         errnum;
  2356.  
  2357.     if (sscanf(str, "%*s %*s %*s [-%d]", &errnum) < 1)
  2358.         errnum = -1;
  2359.     return errnum;
  2360. } /* getAsnError */
  2361.  
  2362.  
  2363. /*
  2364.  * Purpose:     Set the Connection ID file pointer
  2365.  *
  2366.  * Parameters:
  2367.  *   fp           The new value for the file descriptor
  2368.  *
  2369.  *
  2370.  * Description:
  2371.  *              Set the Connection ID file pointer. This is used to update
  2372.  *              the connection ID each time it is updated, to keep the
  2373.  *              value current.
  2374.  *
  2375.  * Note:
  2376.  *              In reality, this should only be called by the dispatcher.
  2377.  */
  2378.  
  2379. void
  2380. SetConFilePtr (FILE *fp)
  2381. {
  2382.     conid_fp = fp;
  2383. }
  2384.  
  2385.  
  2386. /*
  2387.  * Purpose:     Update the connection ID file
  2388.  *
  2389.  * Parameters:
  2390.  *   conid        The new connection ID value
  2391.  *
  2392.  *
  2393.  * Description:
  2394.  *              Update the connection ID file, and be sure to flush the stream,
  2395.  *              to try to ensure that output really occurs.
  2396.  *
  2397.  * Note:
  2398.  *              Should be called every time the "next" connection ID is
  2399.  *              modified.
  2400.  */
  2401.  
  2402. void
  2403. WriteConFile (Uint4 conid)
  2404. {
  2405.     if (conid_fp != NULL) {
  2406.         (void) fseek(conid_fp, 0L, SEEK_SET);
  2407.         (void) FileWrite((CharPtr) &conid, 1, sizeof(conid), conid_fp);
  2408.         (void) fflush (conid_fp);
  2409.     }
  2410. }
  2411.  
  2412.  
  2413. /*
  2414.  * Purpose:     Close the connection ID file
  2415.  *
  2416.  *
  2417.  * Description:
  2418.  *              Close the connection ID file.
  2419.  *              to try to ensure that output really occurs.
  2420.  *
  2421.  * Note:
  2422.  *              In reality, this should only be called by the dispatcher.
  2423.  */
  2424.  
  2425. void
  2426. CloseConFile (void)
  2427. {
  2428.     if (conid_fp != NULL) {
  2429.         fclose (conid_fp);
  2430.         conid_fp = NULL;
  2431.     }
  2432. }
  2433.  
  2434.  
  2435.  
  2436. /*
  2437.  * Purpose:     Check for expired timers
  2438.  *
  2439.  *
  2440.  * Description:
  2441.  *              For every expired timer, call the specified timer
  2442.  *              callback function, which is in turn responsible for cancelling
  2443.  *              the timer.
  2444.  *
  2445.  * Note:
  2446.  *              Timer checks only take place when this function is called.
  2447.  *              Therefore, it is the responsibility of an application to
  2448.  *              intermitently call this function.  This could be done, e.g.
  2449.  *              using the UNIX alarm clock mechanism, or inside of an event
  2450.  *              loop.
  2451.  *
  2452.  *              The order of operations is significant here, because the
  2453.  *              hook function must cancel the timer.  To perform the linked
  2454.  *              list traversal in a less careful manner could result in
  2455.  *              illegal memory accesses.
  2456.  *
  2457.  *              The timer list in managed in a very unsophisticated manner;
  2458.  *              if lots of timers were anticipated, the list would be
  2459.  *              maintained sorted by time, and all of the timer functions
  2460.  *              would need to maintain and traverse the timer list based
  2461.  *              upon this criterion.
  2462.  *
  2463.  *              A count is used as a failsafe mechanism against infinite loops.
  2464.  */
  2465.  
  2466. #define NI_MAX_TIMERS 1000
  2467.  
  2468. void
  2469. NI_ProcessTimers(void)
  2470. {
  2471.     NodePtr t;
  2472.     NodePtr tnew;
  2473.     NI_TimerPtr timer;
  2474.     time_t curtime;
  2475.     int count = NI_MAX_TIMERS;
  2476.  
  2477.     if ((t = timerHead) == NULL)
  2478.     {
  2479.         return;
  2480.     }
  2481.  
  2482.     curtime = GetSecs();
  2483.  
  2484.     do {
  2485.         timer = (NI_TimerPtr) t->elem;
  2486.         tnew = ListGetNext(t);
  2487.         if (timer != NULL && timer->timeout != NULLB &&
  2488.             timer->timeout <= curtime)
  2489.         { /* fire timer */
  2490.             /* mark the timer so it won't fire again */
  2491.             timer->timeout = NULLB;
  2492.             if (timer->hook != NULL)
  2493.             {
  2494.                 timer->hook(timer->hookParam);
  2495.             }
  2496.         }
  2497.         if (t == tnew)
  2498.         { /* data structure error, time to bail out */
  2499.             break;
  2500.         }
  2501.         t = tnew;
  2502.     } while (t != timerHead && t != NULL && --count > 0);
  2503. }
  2504.  
  2505.  
  2506. /*
  2507.  * Purpose:     Return the time when the next timeout will occur
  2508.  *
  2509.  * Returns:     The time, in seconds, when the next scheduled timeout will
  2510.  *              occur, or NULLB, if there are no timers set.
  2511.  *
  2512.  * Description:
  2513.  *              Return the time when the next timer timeout will occur.
  2514.  *              This information is typically used with the select()
  2515.  *              system call, to ensure that a timeout parameter is passed
  2516.  *              to select() which is sufficiently short to ensure that
  2517.  *              the application will call NI_ProcessTimers() at an
  2518.  *              appropriate time.
  2519.  *
  2520.  * Note:
  2521.  *              The timer list in managed in a very unsophisticated manner;
  2522.  *              if lots of timers were anticipated, the list would be
  2523.  *              maintained sorted by time, and all of the timer functions
  2524.  *              would need to maintain and traverse the timer list based
  2525.  *              upon this criterion.
  2526.  */
  2527.  
  2528. time_t
  2529. NI_GetNextWakeup(void)
  2530. {
  2531.     time_t next_wakeup = NULLB;
  2532.     NodePtr t;
  2533.     NI_TimerPtr timer;
  2534.  
  2535.     NI_ProcessTimers();
  2536.  
  2537.     if ((t = timerHead) == NULL)
  2538.     {
  2539.         return NULLB;
  2540.     }
  2541.  
  2542.     do {
  2543.         t = ListGetNext(t);
  2544.         timer = (NI_TimerPtr) t->elem;
  2545.         if (next_wakeup == NULLB || (timer->timeout != NULLB &&
  2546.             timer->timeout < next_wakeup))
  2547.         {
  2548.             next_wakeup = timer->timeout;
  2549.         }
  2550.     } while (t != timerHead && t != NULL);
  2551.  
  2552.     return next_wakeup;
  2553. }
  2554.  
  2555.  
  2556. /*
  2557.  * Purpose:     Set a timer
  2558.  *
  2559.  * Parameters:
  2560.  *   timeout      The time in seconds when the timer should expire
  2561.  *   hook         Callback to be called when (if) the timer expires
  2562.  *   hookParam    Parameter to be passed to caller's hook when the timer expires
  2563.  *
  2564.  *
  2565.  * Returns:     The "timer ID", really a pointer to the timer data structure
  2566.  *
  2567.  *
  2568.  * Description:
  2569.  *              Sets a timer with the appropriate parameters.
  2570.  *
  2571.  * Note:
  2572.  *              The timer list in managed in a very unsophisticated manner;
  2573.  *              if lots of timers were anticipated, the list would be
  2574.  *              maintained sorted by time, and all of the timer functions
  2575.  *              would need to maintain and traverse the timer list based
  2576.  *              upon this criterion.
  2577.  *
  2578.  *              It is the responsibility of the application (usually the
  2579.  *              hook function) to cancel the timer.
  2580.  */
  2581.  
  2582. NodePtr
  2583. NI_SetTimer(time_t timeout, NI_TimeoutHook hook, Pointer hookParam)
  2584. {
  2585.     NodePtr t;
  2586.     NI_TimerPtr timer;
  2587.  
  2588.     timer = (NI_TimerPtr) MemNew(sizeof(NI_Timer));
  2589.     timer->timeout = timeout;
  2590.     timer->hook = hook;
  2591.     timer->hookParam = hookParam;
  2592.     t = ListInsert(timer, timerHead);
  2593.     timerHead = t;
  2594.  
  2595.     return t;
  2596. }
  2597.  
  2598.  
  2599. /*
  2600.  * Purpose:     Cancel a timer
  2601.  *
  2602.  * Parameters:
  2603.  *   timerID      The ID of the timer
  2604.  *
  2605.  *
  2606.  * Description:
  2607.  *              Cancel the specified timer by deleting the entry and its
  2608.  *              associated data structure.
  2609.  *
  2610.  * Note:
  2611.  *              The timer list in managed in a very unsophisticated manner;
  2612.  *              if lots of timers were anticipated, the list would be
  2613.  *              maintained sorted by time, and all of the timer functions
  2614.  *              would need to maintain and traverse the timer list based
  2615.  *              upon this criterion.
  2616.  */
  2617.  
  2618. void
  2619. NI_CancelTimer(NodePtr timerId)
  2620. {
  2621.     if (timerId != NULL)
  2622.     {
  2623.         MemFree (timerId->elem);
  2624.         timerHead = ListDelete(timerId);
  2625.     }
  2626.  
  2627.     NI_ProcessTimers();
  2628. }
  2629.  
  2630.  
  2631. /*
  2632.  * Purpose:     Set an activity hook, to inform the application of key events
  2633.  *
  2634.  * Parameters:
  2635.  *   hook         The hook (callback function)
  2636.  *
  2637.  *
  2638.  * Description:
  2639.  *              Setup a hook function which will subsequently be used to
  2640.  *              inform the application of various events; these currently
  2641.  *              include:
  2642.  *                * Connection to dispatcher
  2643.  *                * Disconnection from dispatcher
  2644.  *                * Service connection
  2645.  *                * Service disconnection
  2646.  *                * Bytes written
  2647.  *                * Bytes read
  2648.  *
  2649.  * Note:
  2650.  *              This hook is global for the running application.
  2651.  */
  2652.  
  2653. void
  2654. NI_SetActivityHook (NI_NetServHook hook)
  2655. {
  2656.     activityHook = hook;
  2657. }
  2658.  
  2659.  
  2660. /*
  2661.  * Purpose:     Return the current activity hook
  2662.  *
  2663.  *
  2664.  * Description:
  2665.  *              Return the current activity hook.  This is only intended
  2666.  *              to be used internally by the Network Services library.
  2667.  *              This function is used to avoid making activityHook into a
  2668.  *              global variable.
  2669.  */
  2670.  
  2671. NI_NetServHook
  2672. NI_ActivityHook (void)
  2673. {
  2674.     return activityHook;
  2675. }
  2676.